1// Copyright (C) MongoDB, Inc. 2014-present. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); you may 4// not use this file except in compliance with the License. You may obtain 5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 7package password 8 9import ( 10 "os" 11 "syscall" 12 "unsafe" 13) 14 15// This file is a mess based primarily on 16// "github.com/howeyc/gopass" 17// "golang.org/x/crypto/ssh/terminal" 18// with extra unistd.h ripped from solaris on amd64 19// 20// TODO: get some of these changes merged into the above two packages 21 22// ioctl constants -- not defined in solaris syscall pkg 23const ( 24 SYS_IOCTL = 54 25 TCGETS = 21517 26 TCSETS = 21518 27 ttyfd = 0 //STDIN 28) 29 30// getTermios reads the current termios settings into the 31// given termios struct. 32func getTermios(term *syscall.Termios) error { 33 _, _, errno := syscall.Syscall(SYS_IOCTL, 34 uintptr(ttyfd), uintptr(TCGETS), 35 uintptr(unsafe.Pointer(term))) 36 if errno != 0 { 37 return os.NewSyscallError("SYS_IOCTL", errno) 38 } 39 return nil 40} 41 42// setTermios applies the supplied termios settings 43func setTermios(term *syscall.Termios) error { 44 _, _, errno := syscall.Syscall(SYS_IOCTL, 45 uintptr(ttyfd), uintptr(TCSETS), 46 uintptr(unsafe.Pointer(term))) 47 if errno != 0 { 48 return os.NewSyscallError("SYS_IOCTL", errno) 49 } 50 return nil 51} 52 53// setRaw puts the terminal into "raw" mode, which takes 54// in all key presses and does not echo them. 55func setRaw(term syscall.Termios) error { 56 termCopy := term 57 termCopy.Iflag &^= syscall.ISTRIP | syscall.INLCR | 58 syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF 59 termCopy.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG 60 return setTermios(&termCopy) 61} 62 63// isTerminal checks if we are reading from a terminal (instead of a pipe). 64func IsTerminal() bool { 65 var termios syscall.Termios 66 _, _, errno := syscall.Syscall(SYS_IOCTL, 67 uintptr(ttyfd), TCGETS, 68 uintptr(unsafe.Pointer(&termios))) 69 return errno == 0 70} 71 72// readChar safely gets one byte from stdin 73func readChar() byte { 74 var originalTerm syscall.Termios 75 if err := getTermios(&originalTerm); err != nil { 76 panic(err) // should not happen on amd64 solaris (untested on sparc) 77 } 78 if err := setRaw(originalTerm); err != nil { 79 panic(err) 80 } 81 defer func() { 82 // make sure we return the termios back to normal 83 if err := setTermios(&originalTerm); err != nil { 84 panic(err) 85 } 86 }() 87 88 // read a single byte then reset the terminal state 89 var singleChar [1]byte 90 if n, err := syscall.Read(ttyfd, singleChar[:]); n == 0 || err != nil { 91 panic(err) 92 } 93 return singleChar[0] 94} 95 96// get password from terminal 97func GetPass() string { 98 // keep reading in characters until we hit a stopping point 99 pass := []byte{} 100 for { 101 ch := readChar() 102 if ch == backspaceKey || ch == deleteKey { 103 if len(pass) > 0 { 104 pass = pass[:len(pass)-1] 105 } 106 } else if ch == carriageReturnKey || ch == newLineKey || ch == eotKey || ch == eofKey { 107 break 108 } else if ch != 0 { 109 pass = append(pass, ch) 110 } 111 } 112 return string(pass) 113} 114