-
Notifications
You must be signed in to change notification settings - Fork 26
/
agent.js
152 lines (132 loc) · 4.66 KB
/
agent.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
Client interface for using private keys from an SSH agent running on the
local machine. If an SSH agent is running, this class can be used to
connect to it and retreive L{PKey} objects which can be used when
attempting to authenticate to remote SSH servers.
Because the SSH agent protocol uses environment variables and unix-domain
sockets, this probably doesn't work on Windows. It does work on most
posix platforms though (Linux and MacOS X, for example).
*/
paramikojs.Agent = function () {
/*
Open a session with the local machine's SSH agent, if one is running.
If no agent is running, initialization will succeed, but L{get_keys}
will return an empty tuple.
@raise SSHException: if an SSH agent is found, but speaks an
incompatible protocol
*/
this.conn = null;
this.keys = [];
if(!(Components && Components.classes)) {
throw new Error("Unable to use OS environment without Mozilla's Components.classes"); //FIXME
}
var userEnvironment = Components.classes["@mozilla.org/process/environment;1"].getService(Components.interfaces.nsIEnvironment);
if (userEnvironment.exists('SSH_AUTH_SOCK') && sys.platform != 'win32') {
var conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM); // todo, fixme, doesn't work right now :-/
var auth_sock = userEnvironment.get('SSH_AUTH_SOCK');
try {
conn.connect(auth_sock);
} catch(ex) {
// probably a dangling env var: the ssh agent is gone
return;
}
this.conn = conn;
} else if (sys.platform == 'win32') {
var win_pageant = new paramikojs.win_pageant();
if (win_pageant.can_talk_to_agent()) {
this.conn = win_pageant.PageantConnection();
} else {
return;
}
} else {
// no agent support
return;
}
var msg = this._send_message(String.fromCharCode(paramikojs.Agent.SSH2_AGENTC_REQUEST_IDENTITIES));
if (msg.ptype != paramikojs.Agent.SSH2_AGENT_IDENTITIES_ANSWER) {
throw new paramikojs.ssh_exception.SSHException('could not get keys from ssh-agent');
}
var max = msg.result.get_int();
for (var x = 0; x < max; ++x) {
this.keys.push(new paramikojs.AgentKey(this, msg.result.get_string()));
msg.result.get_string();
}
}
paramikojs.Agent.SSH2_AGENTC_REQUEST_IDENTITIES = 11;
paramikojs.Agent.SSH2_AGENT_IDENTITIES_ANSWER = 12;
paramikojs.Agent.SSH2_AGENTC_SIGN_REQUEST = 13;
paramikojs.Agent.SSH2_AGENT_SIGN_RESPONSE = 14;
paramikojs.Agent.prototype = {
/*
Close the SSH agent connection.
*/
close : function() {
if (this.conn) {
this.conn.close();
}
this.conn = null;
this.keys = [];
},
/*
Return the list of keys available through the SSH agent, if any. If
no SSH agent was running (or it couldn't be contacted), an empty list
will be returned.
@return: a list of keys available on the SSH agent
@rtype: tuple of L{AgentKey}
*/
get_keys : function() {
return this.keys;
},
_send_message : function(msg) {
var msg = msg.toString();
this.conn.send(struct.pack('>I', msg.length) + msg); // TODO, fixme
var l = this._read_all(4);
msg = new paramikojs.Message(this._read_all(struct.unpack('>I', l)[0]));
return { 'ptype': msg.get_byte().charCodeAt(0), 'result': msg };
},
_read_all : function(wanted) {
var result = this.conn.recv(wanted); // TODO, fixme
while (result.length < wanted) {
if (result.length == 0) {
throw new paramikojs.ssh_exception.SSHException('lost ssh-agent');
}
var extra = this.conn.recv(wanted - result.length);
if (extra.length == 0) {
throw new paramikojs.ssh_exception.SSHException('lost ssh-agent');
}
result += extra;
}
return result;
}
};
/*
Private key held in a local SSH agent. This type of key can be used for
authenticating to a remote server (signing). Most other key operations
work as expected.
*/
paramikojs.AgentKey = function(agent, blob) {
inherit(this, new paramikojs.PKey());
this.agent = agent;
this.blob = blob;
this.name = new paramikojs.Message(blob).get_string();
}
paramikojs.AgentKey.prototype = {
toString : function() {
return this.blob;
},
get_name : function() {
return this.name;
},
sign_ssh_data : function(rng, data, callback) {
var msg = new paramikojs.Message();
msg.add_byte(String.fromCharCode(paramikojs.Agent.SSH2_AGENTC_SIGN_REQUEST));
msg.add_string(this.blob);
msg.add_string(data);
msg.add_int(0);
var msg = this.agent._send_message(msg);
if (msg.ptype != paramikojs.Agent.SSH2_AGENT_SIGN_RESPONSE) {
throw new paramikojs.ssh_exception.SSHException('key cannot be used for signing');
}
callback(msg.result.get_string());
}
};