func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string) error { var req []byte switch k := s.(type) { case *rsa.PrivateKey: if len(k.Primes) != 2 { return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes)) } k.Precompute() req = ssh.Marshal(rsaCertMsg{ Type: cert.Type(), CertBytes: cert.Marshal(), D: k.D, Iqmp: k.Precomputed.Qinv, P: k.Primes[0], Q: k.Primes[1], Comments: comment, }) case *dsa.PrivateKey: req = ssh.Marshal(dsaCertMsg{ Type: cert.Type(), CertBytes: cert.Marshal(), X: k.X, Comments: comment, }) case *ecdsa.PrivateKey: req = ssh.Marshal(ecdsaCertMsg{ Type: cert.Type(), CertBytes: cert.Marshal(), D: k.D, Comments: comment, }) default: return fmt.Errorf("agent: unsupported key type %T", s) } signer, err := ssh.NewSignerFromKey(s) if err != nil { return err } if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { return errors.New("agent: signer and cert have different public key") } resp, err := c.call(req) if err != nil { return err } if _, ok := resp.(*successAgentMsg); ok { return nil } return errors.New("agent: failure") }
// Insert adds a private key to the agent. func (c *client) insertKey(s interface{}, comment string) error { var req []byte switch k := s.(type) { case *rsa.PrivateKey: if len(k.Primes) != 2 { return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes)) } k.Precompute() req = ssh.Marshal(rsaKeyMsg{ Type: ssh.KeyAlgoRSA, N: k.N, E: big.NewInt(int64(k.E)), D: k.D, Iqmp: k.Precomputed.Qinv, P: k.Primes[0], Q: k.Primes[1], Comments: comment, }) case *dsa.PrivateKey: req = ssh.Marshal(dsaKeyMsg{ Type: ssh.KeyAlgoDSA, P: k.P, Q: k.Q, G: k.G, Y: k.Y, X: k.X, Comments: comment, }) case *ecdsa.PrivateKey: nistID := fmt.Sprintf("nistp%d", k.Params().BitSize) req = ssh.Marshal(ecdsaKeyMsg{ Type: "ecdsa-sha2-" + nistID, Curve: nistID, KeyBytes: elliptic.Marshal(k.Curve, k.X, k.Y), D: k.D, Comments: comment, }) default: return fmt.Errorf("agent: unsupported key type %T", s) } resp, err := c.call(req) if err != nil { return err } if _, ok := resp.(*successAgentMsg); ok { return nil } return errors.New("agent: failure") }
func marshalKey(k *Key) []byte { var record struct { Blob []byte Comment string } record.Blob = k.Marshal() record.Comment = k.Comment return ssh.Marshal(&record) }
func (s *server) processRequestBytes(reqData []byte) []byte { rep, err := s.processRequest(reqData) if err != nil { if err != errLocked { // TODO(hanwen): provide better logging interface? log.Printf("agent %d: %v", reqData[0], err) } return []byte{agentFailure} } if err == nil && rep == nil { return []byte{agentSuccess} } return ssh.Marshal(rep) }
func (s *sshServer) run(c *gc.C) { netconn, err := s.listener.Accept() c.Assert(err, gc.IsNil) defer func() { err = netconn.Close() c.Assert(err, gc.IsNil) }() conn, chans, reqs, err := cryptossh.NewServerConn(netconn, s.cfg) c.Assert(err, gc.IsNil) s.client = cryptossh.NewClient(conn, chans, reqs) var wg sync.WaitGroup defer wg.Wait() sessionChannels := s.client.HandleChannelOpen("session") c.Assert(sessionChannels, gc.NotNil) for newChannel := range sessionChannels { c.Assert(newChannel.ChannelType(), gc.Equals, "session") channel, reqs, err := newChannel.Accept() c.Assert(err, gc.IsNil) wg.Add(1) go func() { defer wg.Done() defer channel.Close() for req := range reqs { switch req.Type { case "exec": c.Assert(req.WantReply, jc.IsTrue) n := binary.BigEndian.Uint32(req.Payload[:4]) command := string(req.Payload[4 : n+4]) c.Assert(command, gc.Equals, testCommandFlat) req.Reply(true, nil) channel.Write([]byte("abc value\n")) _, err := channel.SendRequest("exit-status", false, cryptossh.Marshal(&struct{ n uint32 }{0})) c.Assert(err, gc.IsNil) return default: c.Fatalf("Unexpected request type: %v", req.Type) } } }() } }
// Sign has the agent sign the data using a protocol 2 key as defined // in [PROTOCOL.agent] section 2.6.2. func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { req := ssh.Marshal(signRequestAgentMsg{ KeyBlob: key.Marshal(), Data: data, }) msg, err := c.call(req) if err != nil { return nil, err } switch msg := msg.(type) { case *signResponseAgentMsg: var sig ssh.Signature if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil { return nil, err } return &sig, nil case *failureAgentMsg: return nil, errors.New("agent: failed to sign challenge") } panic("unreachable") }
func Echo(ws *websocket.Conn) { ip := ws.Request().URL.Query().Get("ip") fmt.Println("conn ip", ip) port := 22 username := "******" password := "******" authMethods := []ssh.AuthMethod{} keyboardInteractiveChallenge := func( user, instruction string, questions []string, echos []bool, ) (answers []string, err error) { if len(questions) == 0 { return []string{}, nil } return []string{password}, nil } authMethods = append(authMethods, ssh.KeyboardInteractive(keyboardInteractiveChallenge)) authMethods = append(authMethods, ssh.Password(password)) sshConfig := &ssh.ClientConfig{ User: username, Auth: authMethods, } sshConfig.Config.SetDefaults() sshConfig.Config.Ciphers = append(sshConfig.Config.Ciphers, "arcfour") fmt.Println(sshConfig.Config.Ciphers) conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%v", ip, port), sshConfig) if err != nil { fmt.Println("1Unable to connect %s", err) return } defer conn.Close() cmd, err := conn.NewSession() if err != nil { fmt.Println("Can't NewSession", err) return } modes := ssh.TerminalModes{ ssh.ECHO: 1, // disable echoing ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud } // Request pseudo terminal if err := cmd.RequestPty("xterm", 80, 40, modes); err != nil { fmt.Println("request for pseudo terminal failed: %s", err) } cmd.Stdout = &Writer{Conn: ws} cmd.Stderr = &Writer{Conn: ws} stdin, err := cmd.StdinPipe() if err != nil { fmt.Println("Can't get stdin") return } err = cmd.Shell() if err != nil { fmt.Println("Can't start cmd:", err) return } for { var reply string if err = websocket.Message.Receive(ws, &reply); err != nil { fmt.Println("Can't receive") break } fmt.Println("Received back from client: " + reply) request := make(map[string]interface{}, 0) err := json.Unmarshal([]byte(reply), &request) fmt.Println("Received back from client: ", request) if err == nil { switch request["cmd"].(string) { case "window-change": w, _ := strconv.Atoi(fmt.Sprint(request["width"])) h, _ := strconv.Atoi(fmt.Sprint(request["height"])) if h > 0 && w > 0 { req := ptyWindowChangeMsg{ Columns: uint32(w), Rows: uint32(h), Width: uint32(w * 8), Height: uint32(h * 8), } fmt.Println("Send window-change:", req) ok, err := cmd.SendRequest("window-change", true, ssh.Marshal(&req)) // Request pseudo terminal if err == nil && !ok { fmt.Println("request for pseudo terminal failed: %v", err) } } case "shell": fmt.Println("Write:", request["input"]) stdin.Write([]byte(request["input"].(string))) case "ping": req := ptyEnvMsg{Name: "__keep_alive__", Value: "1"} cmd.SendRequest("env", true, ssh.Marshal(&req)) resp := WsResponse{Command: "pong", Parameters: map[string]interface{}{}} json_bytes, err := json.Marshal(resp) if err != nil { fmt.Println("Marshal error:", err) return } websocket.Message.Send(ws, string(json_bytes)) } } else { fmt.Println(err) } } }
func (c *client) Unlock(passphrase []byte) error { req := ssh.Marshal(&agentUnlockMsg{ Passphrase: passphrase, }) return c.simpleCall(req) }
func (c *client) Remove(key ssh.PublicKey) error { req := ssh.Marshal(&agentRemoveIdentityMsg{ KeyBlob: key.Marshal(), }) return c.simpleCall(req) }
func handleChannel(conn *ssh.ServerConn, newChan ssh.NewChannel) { ch, reqs, err := newChan.Accept() if err != nil { log.Println("newChan.Accept failed:", err) return } defer ch.Close() for req := range reqs { switch req.Type { case "exec": fail := func(at string, err error) { log.Printf("%s failed: %s", at, err) ch.Stderr().Write([]byte("Internal error.\n")) } if req.WantReply { req.Reply(true, nil) } cmdline := string(req.Payload[4:]) cmdargs, err := shlex.Split(cmdline) if err != nil || len(cmdargs) != 2 { ch.Stderr().Write([]byte("Invalid arguments.\n")) return } if cmdargs[0] != "git-receive-pack" { ch.Stderr().Write([]byte("Only `git push` is supported.\n")) return } cmdargs[1] = strings.TrimSuffix(strings.TrimPrefix(cmdargs[1], "/"), ".git") if strings.Contains(cmdargs[1], "..") { ch.Stderr().Write([]byte("Invalid repo.\n")) return } if err := ensureCacheRepo(cmdargs[1]); err != nil { fail("ensureCacheRepo", err) return } cmd := exec.Command("git-shell", "-c", cmdargs[0]+" '"+cmdargs[1]+"'") cmd.Dir = *repoPath cmd.Env = append(os.Environ(), "RECEIVE_USER="******"RECEIVE_REPO="+cmdargs[1], ) done, err := attachCmd(cmd, ch, ch.Stderr(), ch) if err != nil { fail("attachCmd", err) return } if err := cmd.Start(); err != nil { fail("cmd.Start", err) return } done.Wait() status, err := exitStatus(cmd.Wait()) if err != nil { fail("exitStatus", err) return } if _, err := ch.SendRequest("exit-status", false, ssh.Marshal(&status)); err != nil { fail("sendExit", err) } return case "env": if req.WantReply { req.Reply(true, nil) } } } }
func (s *server) processRequest(data []byte) (interface{}, error) { switch data[0] { case agentRequestV1Identities: return &agentV1IdentityMsg{0}, nil case agentRemoveIdentity: var req agentRemoveIdentityMsg if err := ssh.Unmarshal(data, &req); err != nil { return nil, err } var wk wireKey if err := ssh.Unmarshal(req.KeyBlob, &wk); err != nil { return nil, err } return nil, s.agent.Remove(&Key{Format: wk.Format, Blob: req.KeyBlob}) case agentRemoveAllIdentities: return nil, s.agent.RemoveAll() case agentLock: var req agentLockMsg if err := ssh.Unmarshal(data, &req); err != nil { return nil, err } return nil, s.agent.Lock(req.Passphrase) case agentUnlock: var req agentLockMsg if err := ssh.Unmarshal(data, &req); err != nil { return nil, err } return nil, s.agent.Unlock(req.Passphrase) case agentSignRequest: var req signRequestAgentMsg if err := ssh.Unmarshal(data, &req); err != nil { return nil, err } var wk wireKey if err := ssh.Unmarshal(req.KeyBlob, &wk); err != nil { return nil, err } k := &Key{ Format: wk.Format, Blob: req.KeyBlob, } sig, err := s.agent.Sign(k, req.Data) // TODO(hanwen): flags. if err != nil { return nil, err } return &signResponseAgentMsg{SigBlob: ssh.Marshal(sig)}, nil case agentRequestIdentities: keys, err := s.agent.List() if err != nil { return nil, err } rep := identitiesAnswerAgentMsg{ NumKeys: uint32(len(keys)), } for _, k := range keys { rep.Keys = append(rep.Keys, marshalKey(k)...) } return rep, nil case agentAddIdentity: return nil, s.insertIdentity(data) } return nil, fmt.Errorf("unknown opcode %d", data[0]) }
// handleChannel runs the needed commands on a given SSH server connection against the user // that opened the communication channel. func handleChannel(conn *ssh.ServerConn, newChan ssh.NewChannel) { ch, reqs, err := newChan.Accept() if err != nil { log.Println("newChan.Accept failed:", err) return } defer ch.Close() for req := range reqs { switch req.Type { case "exec": assert := func(at string, err error) bool { if err != nil { log.Printf("%s failed: %s", at, err) ch.Stderr().Write([]byte("Internal error.\n")) return true } return false } defer func() { log.Printf("Connection lost from %s", conn.RemoteAddr().String()) }() if req.WantReply { req.Reply(true, nil) } cmdline := string(req.Payload[4:]) cmdargs, err := shlex.Split(cmdline) if assert("shlex.Split", err) { return } if len(cmdargs) != 2 { ch.Stderr().Write([]byte("Invalid arguments.\n")) return } if cmdargs[0] != "git-receive-pack" { ch.Stderr().Write([]byte("Only `git push` is supported.\n")) return } user := conn.Permissions.Extensions["user"] reponame := strings.TrimSuffix(strings.TrimPrefix(cmdargs[1], "/"), ".git") log.Printf("Push from %s at %s", user, reponame) if err := makeGitRepo(reponame); err != nil { ch.Stderr().Write([]byte("Error: " + err.Error())) return } log.Printf("Writing hooks...") err = ioutil.WriteFile(reponame+"/hooks/pre-receive", []byte(`#!/bin/bash strip_remote_prefix() { sed -u "s/^/"$'\e[1G'"/" } set -eo pipefail; while read oldrev newrev refname; do /app/cloudchaser -etcd-machine `+*etcduplink+` pre $newrev 2>&1 | strip_remote_prefix done`), 0755) if err != nil { return } err = ioutil.WriteFile(reponame+"/hooks/post-receive", []byte(`#!/bin/bash strip_remote_prefix() { sed -u "s/^/"$'\e[1G'"/" } set -eo pipefail; while read oldrev newrev refname; do /app/builder -etcd-host `+*etcduplink+` $REPO ${refname##*/} $newrev 2>&1 | strip_remote_prefix done`), 0755) if err != nil { return } log.Printf("Doing git receive...") receive := exec.Command("git-receive-pack", reponame) if conn.Permissions.Extensions["environ"] != "" { receive.Env = append(receive.Env, strings.Split(conn.Permissions.Extensions["environ"], "\n")...) } receive.Env = append(receive.Env, "USER="******"user"]) receive.Env = append(receive.Env, "REMOTE_HOST="+conn.RemoteAddr().String()) receive.Env = append(receive.Env, "REPO="+reponame) done, err := attachCmd(receive, ch, ch.Stderr(), ch) if err != nil { ch.Stderr().Write([]byte("Error: " + err.Error())) return } if assert("receive.Start", receive.Start()) { return } done.Wait() log.Printf("Receive done") status, rcvErr := exitStatus(receive.Wait()) if rcvErr != nil { ch.Stderr().Write([]byte("Error: " + rcvErr.Error())) return } _, err = ch.SendRequest("exit-status", false, ssh.Marshal(&status)) assert("sendExit", err) return case "env": if req.WantReply { req.Reply(true, nil) } default: return } } }
func processRequests(conn *ssh.ServerConn, reqs <-chan *ssh.Request) { for req := range reqs { if req.Type != "tcpip-forward" { // accept only tcpip-forward requests if req.WantReply { req.Reply(false, nil) } continue } type channelForwardMsg struct { Laddr string Lport uint32 } m := &channelForwardMsg{} ssh.Unmarshal(req.Payload, m) privateBytes, err := ioutil.ReadFile(appConfig.RemotePrivateKeyPath) if err != nil { log.Fatal(err.Error()) } signer, err := ssh.ParsePrivateKey(privateBytes) if err != nil { log.Fatal(err.Error()) } config := &ssh.ClientConfig{ User: appConfig.RemoteSSHUser, Auth: []ssh.AuthMethod{ ssh.PublicKeys(signer), }, } sshClientConn, err := ssh.Dial("tcp", appConfig.RemoteSSHAddr, config) if err != nil { log.Fatal(err.Error()) } type channelOpenForwardMsg struct { raddr string rport uint32 laddr string lport uint32 } fm := &channelOpenForwardMsg{ raddr: "localhost", rport: m.Lport, laddr: "localhost", lport: m.Lport, } channel, reqs, err := conn.Conn.OpenChannel("forwarded-tcpip", ssh.Marshal(fm)) if err != nil { log.Fatal(err.Error()) } go ssh.DiscardRequests(reqs) portListener, err := sshClientConn.Listen("tcp", appConfig.RemoteForwardAddress) if err != nil { log.Fatal(err.Error()) } go func() { for { sshConn, err := portListener.Accept() if err != nil { log.Fatal(err.Error()) } // Copy localConn.Reader to sshConn.Writer go func(sshConn net.Conn) { _, err := io.Copy(sshConn, channel) if err != nil { log.Println("io.Copy failed: %v", err) sshConn.Close() return } }(sshConn) // Copy sshConn.Reader to localConn.Writer go func(sshConn net.Conn) { _, err := io.Copy(channel, sshConn) if err != nil { log.Println("io.Copy failed: %v", err) sshConn.Close() return } }(sshConn) } }() req.Reply(true, nil) } }