func newTestServer() (Server, error) { // Create a new backend object. b := redis.New("test", "tcp", "127.0.0.1:6379") // Create a new crypter. c, err := crypto.NewRandomCrypter() if err != nil { return nil, err } private, _ := ssh.ParsePrivateKey(ServerTestPrivateKey) if err != nil { return nil, err } s := NewServer(b, c, private) for _, publicKey := range ServerTestPublicKeys { publicKeyParsed, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publicKey)) if err != nil { return s, err } s.AddReadKey(publicKeyParsed) s.AddWriteKey(publicKeyParsed) } return s, nil }
func ReadUserAuthKeys(u string) (publicKeys []ssh.PublicKey, err error) { uacc, err := user.Lookup(u) if err != nil { return } b, err := ioutil.ReadFile(path.Join(uacc.HomeDir, ".ssh/authorized_keys")) if err != nil { return } buf := bytes.NewBuffer(b) for { line, err := buf.ReadBytes('\n') if err == io.EOF { break } if err != nil { return nil, err } if strings.HasPrefix(string(line), "#") { continue } publicKey, _, _, _, err := ssh.ParseAuthorizedKey(line) if err != nil { return nil, err } publicKeys = append(publicKeys, publicKey) } return }
func CheckHostKey(HostKey string) (checkHostKey func(string, net.Addr, ssh.PublicKey) error) { var err error var public ssh.PublicKey var publices []ssh.PublicKey rest := []byte(HostKey) for { public, _, _, rest, err = ssh.ParseAuthorizedKey(rest) if err != nil { err = nil break } publices = append(publices, public) } checkHostKey = func(hostname string, remote net.Addr, key ssh.PublicKey) (err error) { hostkey := key.Marshal() log.Debug("remote hostkey: %s, type: %s", hostname, key.Type()) for _, public := range publices { if key.Type() == public.Type() && bytes.Compare(hostkey, public.Marshal()) == 0 { log.Info("host key match: %s", hostname) return nil } } log.Info("host key not match: %s", hostname) return ErrHostKey } return }
func TestAgentForward(t *testing.T) { server := newServer(t) defer server.Shutdown() conn := server.Dial(clientConfig()) defer conn.Close() keyring := agent.NewKeyring() keyring.Add(testPrivateKeys["dsa"], nil, "") pub := testPublicKeys["dsa"] sess, err := conn.NewSession() if err != nil { t.Fatalf("NewSession: %v", err) } if err := agent.RequestAgentForwarding(sess); err != nil { t.Fatalf("RequestAgentForwarding: %v", err) } if err := agent.ForwardToAgent(conn, keyring); err != nil { t.Fatalf("SetupForwardKeyring: %v", err) } out, err := sess.CombinedOutput("ssh-add -L") if err != nil { t.Fatalf("running ssh-add: %v, out %s", err, out) } key, _, _, _, err := ssh.ParseAuthorizedKey(out) if err != nil { t.Fatalf("ParseAuthorizedKey(%q): %v", out, err) } if !bytes.Equal(key.Marshal(), pub.Marshal()) { t.Fatalf("got key %s, want %s", ssh.MarshalAuthorizedKey(key), ssh.MarshalAuthorizedKey(pub)) } }
func TestSSHFingerprint(t *testing.T) { Convey("Test generating SSH fingerprint", t, func() { pubkey, _, _, _, err := ssh.ParseAuthorizedKey([]byte("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDXg9Vmhy9YSB8BcN3yHgQjdX9lN3j2KRpv7kVDXSiIana2WbKP7IiTS0uJcJWUM3vlHjdL9KOO0jCWWzVFIcmLhiVVG+Fy2tothBp/NhjR8WWG/6Jg/6tXvVkLG6bDgfbDaLWdE5xzjL0YG8TrIluqnu0J5GHKrQcXF650PlqkGo+whpXrS8wOG+eUmsHX9L1w/Z3TkQlMjQNJEoRbqqSrp7yGj4JqzbtLpsglPRlobD7LHp+5ZDxzpk9i+6hoMxp2muDFxnEtZyED6IMQlNNEGkc3sdmGPOo26oW2+ePkBcjpOpdVif/Iya/jDLuLFHAOol6G34Tr4IdTgaL0qCCr TEST KEY")) panic_the_err(err) fingerprint := SSHFingerprint(pubkey) So(fingerprint, ShouldEqual, "c0:61:84:fc:e8:c9:52:dc:cd:a9:8e:82:a2:70:0a:30") }) }
func TestEncode(t *testing.T) { Convey("Test Encode A String", t, func() { pubkey, _, _, _, err := ssh.ParseAuthorizedKey([]byte("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDXg9Vmhy9YSB8BcN3yHgQjdX9lN3j2KRpv7kVDXSiIana2WbKP7IiTS0uJcJWUM3vlHjdL9KOO0jCWWzVFIcmLhiVVG+Fy2tothBp/NhjR8WWG/6Jg/6tXvVkLG6bDgfbDaLWdE5xzjL0YG8TrIluqnu0J5GHKrQcXF650PlqkGo+whpXrS8wOG+eUmsHX9L1w/Z3TkQlMjQNJEoRbqqSrp7yGj4JqzbtLpsglPRlobD7LHp+5ZDxzpk9i+6hoMxp2muDFxnEtZyED6IMQlNNEGkc3sdmGPOo26oW2+ePkBcjpOpdVif/Iya/jDLuLFHAOol6G34Tr4IdTgaL0qCCr TEST KEY")) panic_the_err(err) plaintext := "some plaintext" ciphertext, err := CredulousEncode(plaintext, pubkey) So(err, ShouldEqual, nil) So(len(ciphertext), ShouldEqual, 556) }) }
// ParseAuthorisedKey parses a non-comment line from an // authorized_keys file and returns the constituent parts. // Based on description in "man sshd". func ParseAuthorisedKey(line string) (*AuthorisedKey, error) { key, comment, _, _, ok := ssh.ParseAuthorizedKey([]byte(line)) if !ok { return nil, fmt.Errorf("invalid authorized_key %q", line) } keyBytes := ssh.MarshalPublicKey(key) return &AuthorisedKey{ Key: keyBytes, Comment: comment, }, nil }
// ParseAuthorisedKey parses a non-comment line from an // authorized_keys file and returns the constituent parts. // Based on description in "man sshd". func ParseAuthorisedKey(line string) (*AuthorisedKey, error) { key, comment, _, _, err := ssh.ParseAuthorizedKey([]byte(line)) if err != nil { return nil, fmt.Errorf("invalid authorized_key %q", line) } return &AuthorisedKey{ Type: key.Type(), Key: key.Marshal(), Comment: comment, }, nil }
func readSSHPubkeyFile(filename string) (pubkey ssh.PublicKey, err error) { pubkeyString, err := ioutil.ReadFile(filename) if err != nil { return nil, err } pubkey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(pubkeyString)) if err != nil { return nil, err } return pubkey, nil }
func newKey(name, user, raw string) (*Key, error) { key, comment, _, _, ok := ssh.ParseAuthorizedKey([]byte(raw)) if !ok { return nil, ErrInvalidKey } body := ssh.MarshalAuthorizedKey(key) k := Key{ Name: name, Body: string(body), Comment: comment, UserName: user, } return &k, nil }
func newKey(name, user, raw string) (*Key, error) { key, comment, _, _, err := ssh.ParseAuthorizedKey([]byte(raw)) if err != nil { return nil, ErrInvalidKey } body := ssh.MarshalAuthorizedKey(key.(ssh.PublicKey)) k := Key{ Name: name, Body: string(body), Comment: comment, UserName: user, CreatedAt: time.Now(), } return &k, nil }
func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []authResult) { rest := authKeys var values []authResult for len(rest) > 0 { var r authResult r.pubKey, r.comments, r.options, rest, r.ok = ssh.ParseAuthorizedKey(rest) r.rest = string(rest) values = append(values, r) } if !reflect.DeepEqual(values, expected) { t.Errorf("got %q, expected %q", values, expected) } }
// TestHostKeyChecker tests to check existing key func TestHostKeyChecker(t *testing.T) { keyFile := NewHostKeyFile(hostFile) checker := NewHostKeyChecker(keyFile) addr, key, _ := parseKnownHostsLine([]byte(hostLine)) tcpAddr, _ := net.ResolveTCPAddr("tcp", addr) if err := checker.Check("localhost", tcpAddr, key); err != nil { t.Fatalf("checker should succeed for %v: %v", tcpAddr.String(), err) } wrongKey, _, _, _, _ := gossh.ParseAuthorizedKey([]byte(wrongAuthorizedKey)) if err := checker.Check("localhost", tcpAddr, wrongKey); err != ErrUnmatchKey { t.Fatalf("checker should fail with %v", ErrUnmatchKey) } }
func serverFetchPublicKeys(url string) ([]ssh.PublicKey, error) { // Fetch the public keys. response, err := serverClient.Get(url) if err != nil { return nil, err } // Read out the entire body. jsonResponse, err := ioutil.ReadAll(response.Body) if err != nil { return nil, err } // Build a raw keys object that reflects the expected structure of the JSON. var rawKeys []struct { Key string } // Try to parse the body of the response as JSON. if err := json.Unmarshal(jsonResponse, &rawKeys); err != nil { return nil, err } // Build a new authorizer and iterate through the raw keys, parsing them // and then adding them. publicKeys := make([]ssh.PublicKey, 0, len(rawKeys)) for _, rawKey := range rawKeys { // We're only interested in the key itself and whether or not there was an error. publicKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(rawKey.Key)) if err != nil { return publicKeys, err } // Add the key to the list. publicKeys = publicKeys[:len(publicKeys)+1] publicKeys[len(publicKeys)-1] = publicKey } return publicKeys, nil }
// Read SSH public key bytes from path func sshReadPubKey(path string) (ssh.PublicKey, string, error) { f, err := os.Open(filepath.FromSlash(path)) if err != nil { return nil, "", err } keyBytes, err := ioutil.ReadAll(f) if err != nil { return nil, "", err } if bytes.Contains(keyBytes, []byte("PRIVATE")) { return nil, "", errPrivKey(path) } key, comment, _, _, ok := ssh.ParseAuthorizedKey(keyBytes) if !ok { return nil, "", errNotKey } return key, comment, nil }
func TestMarshalParsePublicKey(t *testing.T) { pub := getTestPublicKey(t) authKeys := ssh.MarshalAuthorizedKey(pub) actualFields := strings.Fields(string(authKeys)) if len(actualFields) == 0 { t.Fatalf("failed authKeys: %v", authKeys) } // drop the comment expectedFields := strings.Fields(keys["authorized_keys"])[0:2] if !reflect.DeepEqual(actualFields, expectedFields) { t.Errorf("got %v, expected %v", actualFields, expectedFields) } actPub, _, _, _, ok := ssh.ParseAuthorizedKey([]byte(keys["authorized_keys"])) if !ok { t.Fatalf("cannot parse %v", keys["authorized_keys"]) } if !reflect.DeepEqual(actPub, pub) { t.Errorf("got %v, expected %v", actPub, pub) } }
// Find SSH keys on the local file system func getSSHKeys(existingKeyFingerprints map[string]bool) map[string]string { candidateKeys := make(map[string]string) // get key from id_rsa.pub rsaKey, rsaComment, rsaErr := sshReadPubKey(idRsaPubPath) if rsaErr == nil { candidateKeys[string(ssh.MarshalPublicKey(rsaKey))] = rsaComment } // get key from id_dsa.pub dsaKey, dsaComment, dsaErr := sshReadPubKey(idDsaPubPath) if dsaErr == nil { candidateKeys[string(ssh.MarshalPublicKey(dsaKey))] = dsaComment } // get keys from ssh-add out, err := exec.Command("ssh-add", "-L").Output() sshAddKeys := strings.TrimSpace(string(out)) if err == nil && sshAddKeys != "" { for _, k := range strings.Split(sshAddKeys, "\n") { key, comment, _, _, ok := ssh.ParseAuthorizedKey([]byte(k)) if ok { candidateKeys[string(ssh.MarshalPublicKey(key))] = comment } } } for k, _ := range candidateKeys { pubKey, _, _ := ssh.ParsePublicKey([]byte(k)) fp := fingerprint(pubKey) if _, ok := existingKeyFingerprints[fp]; ok { delete(candidateKeys, k) } } return candidateKeys }
// parseKnownHostsLine parses a line from a known hosts file. It returns a // string containing the hosts section of the line, a gossh.PublicKey parsed // from the line, and any error encountered during the parsing. func parseKnownHostsLine(line []byte) (string, gossh.PublicKey, error) { // Skip any leading whitespace. line = bytes.TrimLeft(line, "\t ") // Skip comments and empty lines. if bytes.HasPrefix(line, []byte("#")) || len(line) == 0 { return "", nil, nil } // Skip markers. if bytes.HasPrefix(line, []byte("@")) { return "", nil, errors.New("marker functionality not implemented") } // Find the end of the host name(s) portion. end := bytes.IndexAny(line, "\t ") if end <= 0 { return "", nil, errors.New("bad format (insufficient fields)") } hosts := string(line[:end]) keyBytes := line[end+1:] // Check for hashed host names. if strings.HasPrefix(hosts, sshHashDelim) { return "", nil, errors.New("hashed hosts not implemented") } // Finally, actually try to extract the key. key, _, _, _, err := gossh.ParseAuthorizedKey(keyBytes) if err != nil { return "", nil, fmt.Errorf("error parsing key: %v", err) } return hosts, key, nil }
func main() { usage := `Authorized Keys. Usage: authorized-keys <user> [--force-server=<server>] [--test] authorized-keys -h | --help authorized-keys -v | --version Options: -h --help Show this screen. -v --version Show version. --force-server=<server> Force server name. --test Test mode - no logging, just print users and key fingerprints. ` arguments, err := docopt.Parse(usage, nil, true, "Authorized Keys build "+builddate, false) if err != nil { initError(usage, 2) } var test = false if arguments["--test"] == true { test = true } if arguments["<user>"] == nil { initError("No user defined", 3) } // Load config file const configfile = "/usr/local/etc/authorized-keys.conf" configsource, err := ioutil.ReadFile(configfile) config := configData{} if err != nil { os.Stderr.WriteString("Error opening config file " + configfile + ", using defaults\n") programDir := filepath.Dir(os.Args[0]) config.Data = programDir + "/authorized-keys.yaml" config.Log = programDir + "/authorized-keys.log" } else { err = yaml.Unmarshal(configsource, &config) if err != nil { initError(err.Error(), 4) } } // Open log file if !test { l, err := os.OpenFile(config.Log, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0660) if err != nil { initError("error opening log file: '"+config.Log+"'"+err.Error(), 5) } defer l.Close() log.SetOutput(io.MultiWriter(l, os.Stderr)) } else { log.SetOutput(os.Stderr) } // Username username := fmt.Sprintf("%s", arguments["<user>"]) if !safestring(username) { log.Fatalln("User '" + username + "' is not safe string") } // Hostname (server name) var hostname string if arguments["--force-server"] == nil { hostname, _ = os.Hostname() } else { hostname = fmt.Sprintf("%s", arguments["--force-server"]) } if !safestring(hostname) { log.Fatalln("Host '" + hostname + "' is not safe string") } // Data file source, err := ioutil.ReadFile(config.Data) if err != nil { initError("Can't read data file "+config.Data, 5) } authorizedKeys := authorizedKeys{} err = yaml.Unmarshal(source, &authorizedKeys) if err != nil { initError("Cannot parse yaml file "+err.Error(), 6) } // Debug log.Println("Requested authorized_keys for server '" + hostname + "' and user '" + username + "'") // Find server/user _, ok := authorizedKeys.Access[hostname][username] if !ok { log.Fatalln("Server '" + hostname + "' not found") } // Find aliases for server/user users, ok := authorizedKeys.Access[hostname][username] if !ok { log.Fatalln("No users found") } // Find all keys for aliases var keys []string for _, user := range users { k, ok := authorizedKeys.Users[user]["keys"] if !ok { log.Fatalln("User '" + user + "' doesn't have any keys defined") } // Validate and append each key for index, key := range k { publickey, comment, _, _, err := ssh.ParseAuthorizedKey([]byte(key)) if err == nil { keys = append(keys, key) if test { fingerprint := fmt.Sprintf("%x", md5.Sum(publickey.Marshal())) log.Printf("Using key %q for user %q. Fingerprint: %s", comment, user, formatFingerPrint(fingerprint)) } else { log.Printf("Using key %q for user %q", comment, user) } } else { log.Printf("Skipping key %d for user %q", index, user) } } } // Output for sshd if !test { os.Stdout.WriteString(strings.Join(keys, "\n")) } }
func TestInvalidEntry(t *testing.T) { _, _, _, _, ok := ssh.ParseAuthorizedKey(authInvalid) if ok { t.Errorf("Expected invalid entry, returned valid entry") } }