github.com/jlmucb/cloudproxy@v0.0.0-20170830161738-b5aa0b619bc4/go/apps/host/host.go (about)

     1  // Copyright (c) 2014, Kevin Walsh.  All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package host exposes the functionality of a linux_host implementation as a
    16  // library.
    17  package host
    18  
    19  import (
    20  	"crypto/x509"
    21  	"crypto/x509/pkix"
    22  	"errors"
    23  	"flag"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"net"
    27  	"os"
    28  	"os/exec"
    29  	"os/signal"
    30  	"path"
    31  	"syscall"
    32  	"text/tabwriter"
    33  
    34  	"github.com/golang/protobuf/proto"
    35  	"github.com/jlmucb/cloudproxy/go/tao"
    36  	"github.com/jlmucb/cloudproxy/go/util"
    37  	"github.com/jlmucb/cloudproxy/go/util/options"
    38  	// "github.com/golang/crypto/ssh/terminal"
    39  	"golang.org/x/crypto/ssh/terminal"
    40  )
    41  
    42  var opts = []options.Option{
    43  	// Flags for all/most commands
    44  	{"tao_domain", "", "<dir>", "Tao domain configuration directory", "all"},
    45  	{"host", "", "<dir>", "Host configuration, relative to domain directory or absolute", "all"},
    46  	{"quiet", false, "", "Be more quiet", "all"},
    47  	{"domain_pass", "", "<password>", "Password for domain policy key", "all"},
    48  
    49  	// Flags for init (and start) command
    50  	{"root", false, "", "Create a root host, not backed by any parent Tao", "init,start"},
    51  	{"stacked", false, "", "Create a stacked host, backed by a parent Tao", "init,start"},
    52  	// TODO(kwalsh) hosted program type should be selectable at time of
    53  	// tao_launch. A single host should be able to host all types concurrently.
    54  	{"hosting", "", "<type>", "Hosted program type: process, docker, kvm_coreos or kvm_custom", "init"},
    55  	{"socket_dir", "", "<dir>", "Hosted program socket directory, relative to host directory or absolute", "init"},
    56  
    57  	// Flags for start command
    58  	{"foreground", false, "", "Run in the foreground", "start"},
    59  	// Using setsid (1) and shell redirection is an alternative -daemon:
    60  	//    sh$ setsid tao host start ... </dev/null >/dev/null 2>&1
    61  	//    sh$ setsid linux_host start ... </dev/null >/dev/null 2>&1
    62  	{"daemon", false, "", "Detach from tty, close stdio, and run as a daemon", "start"},
    63  
    64  	// Flags for root
    65  	{"pass", "", "<password>", "Host password for root hosts (for testing only!)", "root"},
    66  
    67  	// Flags for stacked
    68  	{"parent_type", "", "<type>", "Type of channel to parent Tao: TPM, TPM2, pipe, file, or unix", "stacked"},
    69  	{"parent_spec", "", "<spec>", "Spec for channel to parent Tao", "stacked"},
    70  
    71  	// Flags for QEMU/KVM CoreOS init
    72  	{"kvm_coreos_img", "", "<path>", "Path to CoreOS.img file, relative to domain or absolute", "kvm"},
    73  	{"kvm_coreos_vm_memory", 0, "SIZE", "The amount of RAM (in KB) to give VM", "kvm"},
    74  	// TODO(kwalsh) shouldn't keys be generated randomly within the host?
    75  	// Otherwise, we need to trust whoever holds the keys, no?
    76  	{"kvm_coreos_ssh_auth_keys", "", "<path>", "An authorized_keys file for SSH to CoreOS guest, relative to domain or absolute", "kvm"},
    77  
    78  	// Flags for QEMU/KVM init with custom kernel and initram
    79  	{"kvm_custom_vm_memory", 1024, "SIZE", "The amount of RAM (in KB) to give VM", "kvm_custom"},
    80  }
    81  
    82  func init() {
    83  	options.Add(opts...)
    84  }
    85  
    86  func help() {
    87  	w := new(tabwriter.Writer)
    88  	w.Init(os.Stderr, 4, 0, 2, ' ', 0)
    89  	av0 := path.Base(os.Args[0])
    90  
    91  	fmt.Fprintf(w, "Linux Tao Host\n")
    92  	fmt.Fprintf(w, "Usage:\n")
    93  	fmt.Fprintf(w, "  %s init [options]\t Initialize a new host\n", av0)
    94  	fmt.Fprintf(w, "  %s show [options]\t Show host principal name\n", av0)
    95  	fmt.Fprintf(w, "  %s start [options]\t Start the host\n", av0)
    96  	fmt.Fprintf(w, "  %s stop [options]\t Request the host stop\n", av0)
    97  	fmt.Fprintf(w, "\n")
    98  
    99  	categories := []options.Category{
   100  		{"all", "Basic options for most commands"},
   101  		{"init", "Options for 'init' command"},
   102  		{"start", "Options for 'start' command"},
   103  		{"root", "Options for root hosts"},
   104  		{"stacked", "Options for stacked hosts"},
   105  		{"kvm", "Options for hosting QEMU/KVM CoreOS"},
   106  		{"kvm_custom", "Options for hosting QEMU/KVM instance with custom kernel and initram"},
   107  		{"logging", "Options to control log output"},
   108  	}
   109  	options.ShowRelevant(w, categories...)
   110  
   111  	w.Flush()
   112  }
   113  
   114  var noise = ioutil.Discard
   115  
   116  // Main provides the main functionality of linux_host. This is provided as a
   117  // separate function to allow other code to register other Tao implementations
   118  // (with tao.Register) before starting the code.
   119  func Main() {
   120  	flag.Usage = help
   121  
   122  	// Get options before the command verb
   123  	flag.Parse()
   124  	// Get command verb
   125  	cmd := "help"
   126  	if flag.NArg() > 0 {
   127  		cmd = flag.Arg(0)
   128  	}
   129  	// Get options after the command verb
   130  	if flag.NArg() > 1 {
   131  		flag.CommandLine.Parse(flag.Args()[1:])
   132  	}
   133  
   134  	if !*options.Bool["quiet"] {
   135  		noise = os.Stdout
   136  	}
   137  	// Load the domain.
   138  	domain, err := tao.LoadDomain(domainConfigPath(), nil)
   139  
   140  	// Set $TAO_DOMAIN so it will be inherited by hosted programs
   141  	os.Unsetenv("TAO_DOMAIN")
   142  	err = os.Setenv("TAO_DOMAIN", domainPath())
   143  	options.FailIf(err, "Can't set $TAO_DOMAIN")
   144  
   145  	switch cmd {
   146  	case "help":
   147  		help()
   148  	case "init":
   149  		initHost(domain)
   150  	case "show":
   151  		showHost(domain)
   152  	case "start":
   153  		startHost(domain)
   154  	case "stop", "shutdown":
   155  		stopHost(domain)
   156  	default:
   157  		options.Usage("Unrecognized command: %s", cmd)
   158  	}
   159  }
   160  
   161  func domainPath() string {
   162  	if path := *options.String["tao_domain"]; path != "" {
   163  		return path
   164  	}
   165  	if path := os.Getenv("TAO_DOMAIN"); path != "" {
   166  		return path
   167  	}
   168  	options.Usage("Must supply -tao_domain or set $TAO_DOMAIN")
   169  	return ""
   170  }
   171  
   172  func domainConfigPath() string {
   173  	return path.Join(domainPath(), "tao.config")
   174  }
   175  
   176  func hostPath() string {
   177  	hostPath := *options.String["host"]
   178  	if hostPath == "" {
   179  		// options.Usage("Must supply a -host path")
   180  		hostPath = "linux_tao_host"
   181  	}
   182  	if !path.IsAbs(hostPath) {
   183  		hostPath = path.Join(domainPath(), hostPath)
   184  	}
   185  	return hostPath
   186  }
   187  
   188  func hostConfigPath() string {
   189  	return path.Join(hostPath(), "host.config")
   190  }
   191  
   192  // Update configuration based on command-line options. Does very little sanity checking.
   193  func configureFromOptions(cfg *tao.LinuxHostConfig) {
   194  	if *options.Bool["root"] && *options.Bool["stacked"] {
   195  		options.Usage("Can supply only one of -root and -stacked")
   196  	} else if *options.Bool["root"] {
   197  		cfg.Type = proto.String("root")
   198  	} else if *options.Bool["stacked"] {
   199  		cfg.Type = proto.String("stacked")
   200  	} else if cfg.Type == nil {
   201  		options.Usage("Must supply one of -root and -stacked")
   202  	}
   203  	if s := *options.String["hosting"]; s != "" {
   204  		cfg.Hosting = proto.String(s)
   205  	}
   206  	if s := *options.String["parent_type"]; s != "" {
   207  		cfg.ParentType = proto.String(s)
   208  	}
   209  	if s := *options.String["parent_spec"]; s != "" {
   210  		cfg.ParentSpec = proto.String(s)
   211  	}
   212  	if s := *options.String["socket_dir"]; s != "" {
   213  		cfg.SocketDir = proto.String(s)
   214  	}
   215  	if s := *options.String["kvm_coreos_img"]; s != "" {
   216  		cfg.KvmCoreosImg = proto.String(s)
   217  	}
   218  	if i := *options.Int["kvm_coreos_vm_memory"]; i != 0 {
   219  		cfg.KvmCoreosVmMemory = proto.Int32(int32(i))
   220  	}
   221  	if s := *options.String["kvm_coreos_ssh_auth_keys"]; s != "" {
   222  		cfg.KvmCoreosSshAuthKeys = proto.String(s)
   223  	}
   224  	if i := *options.Int["kvm_custom_vm_memory"]; i != 0 {
   225  		cfg.KvmCustomVmMemory = proto.Int32(int32(i))
   226  	}
   227  }
   228  
   229  func configureFromFile() *tao.LinuxHostConfig {
   230  	d, err := ioutil.ReadFile(hostConfigPath())
   231  	if err != nil {
   232  		options.Fail(err, "Can't read linux host configuration")
   233  	}
   234  	var cfg tao.LinuxHostConfig
   235  	if err := proto.UnmarshalText(string(d), &cfg); err != nil {
   236  		options.Fail(err, "Can't parse linux host configuration")
   237  	}
   238  	return &cfg
   239  }
   240  
   241  func loadHost(domain *tao.Domain, cfg *tao.LinuxHostConfig) (*tao.LinuxHost, error) {
   242  	var tc tao.Config
   243  
   244  	// Decide host type
   245  	switch cfg.GetType() {
   246  	case "root":
   247  		tc.HostType = tao.Root
   248  	case "stacked":
   249  		tc.HostType = tao.Stacked
   250  	case "":
   251  		options.Usage("Must supply -hosting flag")
   252  	default:
   253  		options.Usage("Invalid host type: %s", cfg.GetType())
   254  	}
   255  
   256  	// Decide hosting type
   257  	switch cfg.GetHosting() {
   258  	case "process":
   259  		tc.HostedType = tao.ProcessPipe
   260  	case "docker":
   261  		tc.HostedType = tao.DockerUnix
   262  	case "kvm_coreos":
   263  		tc.HostedType = tao.KVMCoreOSFile
   264  	case "kvm_custom":
   265  		tc.HostedType = tao.KVMCustom
   266  	case "":
   267  		options.Usage("Must supply -hosting flag")
   268  	default:
   269  		options.Usage("Invalid hosting type: %s", cfg.GetHosting())
   270  	}
   271  
   272  	// For stacked hosts, figure out the channel type: TPM, TPM2, pipe, file, or unix
   273  	if tc.HostType == tao.Stacked {
   274  		switch cfg.GetParentType() {
   275  		case "TPM":
   276  			tc.HostChannelType = "tpm"
   277  		case "TPM2":
   278  			tc.HostChannelType = "tpm2"
   279  		case "pipe":
   280  			tc.HostChannelType = "pipe"
   281  		case "file":
   282  			tc.HostChannelType = "file"
   283  		case "unix":
   284  			tc.HostChannelType = "unix"
   285  		case "":
   286  			options.Usage("Must supply -parent_type for stacked hosts")
   287  		default:
   288  			options.Usage("Invalid parent type: '%s'", cfg.GetParentType())
   289  		}
   290  
   291  		// For stacked hosts on anything but a TPM, we also need parent spec
   292  		if tc.HostChannelType != "tpm" && tc.HostChannelType != "tpm2" {
   293  			tc.HostSpec = cfg.GetParentSpec()
   294  			if tc.HostSpec == "" {
   295  				options.Usage("Must supply -parent_spec for non-TPM stacked hosts")
   296  			}
   297  		} else if tc.HostChannelType == "tpm" {
   298  			// For stacked hosts on a TPM, we also need info from domain config
   299  			if domain.Config.TpmInfo == nil {
   300  				options.Usage("Must provide TPM configuration in the domain to use a TPM")
   301  			}
   302  			tc.TPMAIKPath = path.Join(domainPath(), domain.Config.TpmInfo.GetAikPath())
   303  			tc.TPMPCRs = domain.Config.TpmInfo.GetPcrs()
   304  			tc.TPMDevice = domain.Config.TpmInfo.GetTpmPath()
   305  			tc.TPMAIKCertPath = path.Join(domainPath(), domain.Config.TpmInfo.GetAikCertPath())
   306  		} else if tc.HostChannelType == "tpm2" {
   307  			// For stacked hosts on a TPM2, we also need info from domain config
   308  			if domain.Config.Tpm2Info == nil {
   309  				options.Usage("Must provide TPM2 configuration in the domain to use a TPM2")
   310  			}
   311  
   312  			tc.TPM2InfoDir = domainPath()
   313  			tc.TPM2PCRs = domain.Config.Tpm2Info.GetTpm2Pcrs()
   314  			tc.TPM2Device = domain.Config.Tpm2Info.GetTpm2Device()
   315  		}
   316  	}
   317  
   318  	rulesPath := ""
   319  	if p := domain.RulesPath(); p != "" {
   320  		rulesPath = path.Join(domainPath(), p)
   321  	}
   322  
   323  	// Create the hosted program factory
   324  	socketPath := hostPath()
   325  	if subPath := cfg.GetSocketDir(); subPath != "" {
   326  		if path.IsAbs(subPath) {
   327  			socketPath = subPath
   328  		} else {
   329  			socketPath = path.Join(socketPath, subPath)
   330  		}
   331  	}
   332  
   333  	// TODO(cjpatton) How do the NewLinuxDockerContainterFactory and the
   334  	// NewLinuxKVMCoreOSFactory need to be modified to support the new
   335  	// CachedGuard? They probably don't.
   336  	var childFactory tao.HostedProgramFactory
   337  	switch tc.HostedType {
   338  	case tao.ProcessPipe:
   339  		childFactory = tao.NewLinuxProcessFactory("pipe", socketPath)
   340  	case tao.DockerUnix:
   341  		childFactory = tao.NewLinuxDockerContainerFactory(socketPath, rulesPath)
   342  	case tao.KVMCoreOSFile:
   343  		sshFile := cfg.GetKvmCoreosSshAuthKeys()
   344  		if sshFile == "" {
   345  			options.Usage("Must specify -kvm_coreos_ssh_auth_keys for hosting QEMU/KVM CoreOS")
   346  		}
   347  		if !path.IsAbs(sshFile) {
   348  			sshFile = path.Join(domainPath(), sshFile)
   349  		}
   350  		sshKeysCfg, err := tao.CloudConfigFromSSHKeys(sshFile)
   351  		options.FailIf(err, "Can't read ssh keys")
   352  
   353  		coreOSImage := cfg.GetKvmCoreosImg()
   354  		if coreOSImage == "" {
   355  			options.Usage("Must specify -kvm_coreos_image for hosting QEMU/KVM CoreOS")
   356  		}
   357  		if !path.IsAbs(coreOSImage) {
   358  			coreOSImage = path.Join(domainPath(), coreOSImage)
   359  		}
   360  
   361  		vmMemory := cfg.GetKvmCoreosVmMemory()
   362  		if vmMemory == 0 {
   363  			vmMemory = 1024
   364  		}
   365  
   366  		cfg := &tao.CoreOSConfig{
   367  			ImageFile:  coreOSImage,
   368  			Memory:     int(vmMemory),
   369  			RulesPath:  rulesPath,
   370  			SSHKeysCfg: sshKeysCfg,
   371  		}
   372  		childFactory, err = tao.NewLinuxKVMCoreOSFactory(socketPath, cfg)
   373  		options.FailIf(err, "Can't create KVM CoreOS factory")
   374  	case tao.KVMCustom:
   375  		vmMemory := cfg.GetKvmCustomVmMemory()
   376  		if vmMemory == 0 {
   377  			vmMemory = 1024
   378  		}
   379  		cfg := &tao.VmConfig{
   380  			Memory:     int(vmMemory),
   381  			SocketPath: socketPath,
   382  		}
   383  		childFactory = tao.NewLinuxKVMCustomFactory(cfg)
   384  	}
   385  
   386  	if tc.HostType == tao.Root {
   387  		pwd := getKey("root host key password", "pass")
   388  		lh, err := tao.NewRootLinuxHost(hostPath(), domain.Guard, pwd, childFactory)
   389  		if err != nil {
   390  			return nil, err
   391  		}
   392  		// Load cert
   393  		rootHost, ok := lh.Host.(*tao.RootHost)
   394  		if !ok {
   395  			return nil, errors.New("Type assertion on newly created root host fails")
   396  		}
   397  		var cert *x509.Certificate
   398  		rawCert, err := ioutil.ReadFile(path.Join(hostPath(), "soft_tao_cert"))
   399  		if err != nil {
   400  			// Create cert signed by policy key
   401  			pwd = getKey("Password for domain policy key", "domain_pass")
   402  			// Load the domain.
   403  			domain, err := tao.LoadDomain(domainConfigPath(), pwd)
   404  			if err != nil {
   405  				return nil, err
   406  			}
   407  			if domain.Keys.SigningKey == nil {
   408  				return nil, errors.New("Domain policy key missing signing key")
   409  			}
   410  			keyName := "Soft Tao Key"
   411  			universalBytes, err :=  domain.Keys.SigningKey.UniversalKeyNameFromSigner()
   412  			if err != nil {
   413  				return nil, errors.New("Can't get universal name " + err.Error())
   414  			}
   415  			universalKeyName :=  fmt.Sprintf("key([%x])", universalBytes)
   416  			// TODO: Check with Kevin on the right way to do this
   417  			subject := &pkix.Name{
   418  				Organization: []string{universalKeyName},
   419  				CommonName:   keyName,
   420  			}
   421  			keyType := tao.SignerTypeFromSuiteName(tao.TaoCryptoSuite)
   422  			if keyType == nil {
   423  				return nil, errors.New("Bad key type")
   424  			}
   425  			pkAlg := tao.PublicKeyAlgFromSignerAlg(*keyType)
   426  			sigAlg := tao.SignatureAlgFromSignerAlg(*keyType)
   427  			if pkAlg < 0 || sigAlg < 0 {
   428  				return nil, errors.New("Bad Alg type")
   429  			}
   430  			verifier := rootHost.GetVerifier()
   431  			if verifier == nil {
   432  				return nil, errors.New("Verifier is nil in loadHost")
   433  			}
   434  			cert, err = domain.Keys.SigningKey.CreateSignedX509(domain.Keys.Cert, 1,
   435  				verifier, pkAlg, sigAlg, subject)
   436  			if err != nil {
   437  				return nil, err
   438  			}
   439  			if err = ioutil.WriteFile(path.Join(hostPath(), "soft_tao_cert"),
   440  				cert.Raw, os.ModePerm); err != nil {
   441  				return nil, err
   442  			}
   443  		} else {
   444  			cert, err = x509.ParseCertificate(rawCert)
   445  			if err != nil {
   446  				return nil, err
   447  			}
   448  		}
   449  		rootHost.LoadCert(cert)
   450  		return lh, nil
   451  	} else {
   452  		parent := tao.ParentFromConfig(tc)
   453  		if parent == nil {
   454  			options.Usage("No host tao available, verify -parent_type or $%s\n", tao.HostChannelTypeEnvVar)
   455  		}
   456  		return tao.NewStackedLinuxHost(hostPath(), domain.Guard, tao.ParentFromConfig(tc), childFactory)
   457  	}
   458  }
   459  
   460  func initHost(domain *tao.Domain) {
   461  	var cfg tao.LinuxHostConfig
   462  
   463  	configureFromOptions(&cfg)
   464  	_, err := loadHost(domain, &cfg)
   465  	options.FailIf(err, "Can't create host")
   466  
   467  	// If we get here, keys were created and flags must be ok.
   468  
   469  	file, err := util.CreatePath(hostConfigPath(), 0777, 0666)
   470  	options.FailIf(err, "Can't create host configuration")
   471  	cs := proto.MarshalTextString(&cfg)
   472  	fmt.Fprint(file, cs)
   473  	file.Close()
   474  }
   475  
   476  func showHost(domain *tao.Domain) {
   477  	cfg := configureFromFile()
   478  	configureFromOptions(cfg)
   479  	host, err := loadHost(domain, cfg)
   480  	options.FailIf(err, "Can't create host")
   481  	fmt.Printf("%v\n", host.HostName())
   482  }
   483  
   484  func isBoolFlagSet(name string) bool {
   485  	f := flag.Lookup(name)
   486  	if f == nil {
   487  		return false
   488  	}
   489  	v, ok := f.Value.(flag.Getter).Get().(bool)
   490  	return ok && v
   491  }
   492  
   493  func daemonize() {
   494  	// For our purposes, "daemon" means being a session leader.
   495  	sid, _, errno := syscall.Syscall(syscall.SYS_GETSID, 0, 0, 0)
   496  	var err error
   497  	if errno != 0 {
   498  		err = errno
   499  	}
   500  	options.FailIf(err, "Can't get process SID")
   501  	if int(sid) != syscall.Getpid() {
   502  		// Go does not support daemonize(), and we can't simply call setsid
   503  		// because PID may be equal to GID. Using exec.Cmd with the Setsid=true
   504  		// will fork, ensuring that PID differs from GID, then call setsid, then
   505  		// exec ourself again in the new session.
   506  		path, err := os.Readlink("/proc/self/exe")
   507  		options.FailIf(err, "Can't get path to self executable")
   508  		// special case: keep stderr if -logtostderr or -alsologtostderr
   509  		stderr := os.Stderr
   510  		if !isBoolFlagSet("logtostderr") && !isBoolFlagSet("alsologtostderr") {
   511  			stderr = nil
   512  		}
   513  		spa := &syscall.SysProcAttr{
   514  			Setsid: true, // Create session.
   515  		}
   516  		daemon := exec.Cmd{
   517  			Path:        path,
   518  			Args:        os.Args,
   519  			Stderr:      stderr,
   520  			SysProcAttr: spa,
   521  		}
   522  		err = daemon.Start()
   523  		options.FailIf(err, "Can't become daemon")
   524  		fmt.Fprintf(noise, "Linux Tao Host running as daemon\n")
   525  		os.Exit(0)
   526  	} else {
   527  		fmt.Fprintf(noise, "Already a session leader?\n")
   528  	}
   529  }
   530  
   531  func startHost(domain *tao.Domain) {
   532  
   533  	if *options.Bool["daemon"] && *options.Bool["foreground"] {
   534  		options.Usage("Can supply only one of -daemon and -foreground")
   535  	}
   536  	if *options.Bool["daemon"] {
   537  		daemonize()
   538  	}
   539  
   540  	cfg := configureFromFile()
   541  	configureFromOptions(cfg)
   542  	host, err := loadHost(domain, cfg)
   543  	options.FailIf(err, "Can't create host")
   544  
   545  	sockPath := path.Join(hostPath(), "admin_socket")
   546  	// Set the socketPath directory go+rx so tao_launch can access sockPath and
   547  	// connect to this linux host, even when tao_launch is run as non-root.
   548  	err = os.Chmod(path.Dir(sockPath), 0755)
   549  	options.FailIf(err, "Can't change permissions")
   550  	uaddr, err := net.ResolveUnixAddr("unix", sockPath)
   551  	options.FailIf(err, "Can't resolve unix socket")
   552  	sock, err := net.ListenUnix("unix", uaddr)
   553  	options.FailIf(err, "Can't create admin socket")
   554  	defer sock.Close()
   555  	err = os.Chmod(sockPath, 0666)
   556  	if err != nil {
   557  		sock.Close()
   558  		options.Fail(err, "Can't change permissions on admin socket")
   559  	}
   560  
   561  	go func() {
   562  		fmt.Fprintf(noise, "Linux Tao Service (%s) started and waiting for requests\n", host.HostName())
   563  		err = tao.NewLinuxHostAdminServer(host).Serve(sock)
   564  		fmt.Fprintf(noise, "Linux Tao Service finished\n")
   565  		sock.Close()
   566  		options.FailIf(err, "Error serving admin requests")
   567  		os.Exit(0)
   568  	}()
   569  
   570  	c := make(chan os.Signal, 1)
   571  	signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGTERM)
   572  	<-c
   573  	fmt.Fprintf(noise, "Linux Tao Service shutting down\n")
   574  	err = shutdown()
   575  	if err != nil {
   576  		sock.Close()
   577  		options.Fail(err, "Can't shut down admin socket")
   578  	}
   579  
   580  	// The above goroutine will normally end by calling os.Exit(), so we
   581  	// can block here indefinitely. But if we get a second kill signal,
   582  	// let's abort.
   583  	fmt.Fprintf(noise, "Waiting for shutdown....\n")
   584  	<-c
   585  	options.Fail(nil, "Could not shut down linux_host")
   586  }
   587  
   588  func stopHost(domain *tao.Domain) {
   589  	err := shutdown()
   590  	if err != nil {
   591  		options.Usage("Couldn't connect to linux_host: %s", err)
   592  	}
   593  }
   594  
   595  func shutdown() error {
   596  	sockPath := path.Join(hostPath(), "admin_socket")
   597  	conn, err := net.DialUnix("unix", nil, &net.UnixAddr{Name: sockPath, Net: "unix"})
   598  	if err != nil {
   599  		return err
   600  	}
   601  	defer conn.Close()
   602  	return tao.NewLinuxHostAdminClient(conn).Shutdown()
   603  }
   604  
   605  func getKey(prompt, name string) []byte {
   606  	if input := *options.String[name]; input != "" {
   607  		fmt.Fprintf(os.Stderr, "Warning: Passwords on the command line are not secure. Use -%s option only for testing.\n", name)
   608  		return []byte(input)
   609  	} else {
   610  		// Get the password from the user.
   611  		fmt.Print(prompt + ": ")
   612  		pwd, err := terminal.ReadPassword(syscall.Stdin)
   613  		options.FailIf(err, "Can't get password")
   614  		fmt.Println()
   615  		return pwd
   616  	}
   617  }