Applied Andy Herkey's ipmodem password patch [round 1]
[barry/pauldeden.git] / src / m_ipmodem.cc
blobd389da4d3b9e61fa02f1e436457afb2c6a3f29a2
1 ///
2 /// \file m_ipmodem.cc
3 /// Mode class for GPRS modem mode (using endpoints on
4 /// modern devices)
5 ///
7 /*
8 Copyright (C) 2008, Net Direct Inc. (http://www.netdirect.ca/)
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 See the GNU General Public License in the COPYING file at the
20 root directory of this project for more details.
23 #include "m_ipmodem.h"
24 #include "controller.h"
25 #include "data.h"
26 #include "debug.h"
27 #include <sstream>
28 #include <string.h>
29 #include <openssl/sha.h>
31 namespace Barry { namespace Mode {
33 const char special_flag[] = { 0x78, 0x56, 0x34, 0x12 }; // 0x12345678
34 const char start[] = { 0x01, 0, 0, 0, 0x78, 0x56, 0x34, 0x12 };
35 const char pw_start[] = { 0x01, 0, 0, 0, 1, 0, 0, 0, 0x78, 0x56, 0x34, 0x12 };
37 // Global variable ok?
38 unsigned char session_key[] = { 0x00, 0, 0, 0, 0, 0, 0, 0 };
40 //////////////////////////////////////////////////////////////////////////////
41 // Mode::IpModem class
43 IpModem::IpModem(Controller &con,
44 DeviceDataCallback callback,
45 void *callback_context)
46 : m_con(con)
47 , m_dev(con.m_dev)
48 , m_continue_reading(false)
49 , m_callback(callback)
50 , m_callback_context(callback_context)
54 IpModem::~IpModem()
56 // thread running?
57 if( m_continue_reading ) {
58 m_continue_reading = false;
59 pthread_join(m_modem_read_thread, NULL);
63 bool IpModem::SendPassword(const char *password)
65 if( strlen(password) == 0 ) {
66 throw BadPassword("No password specified.", 0, false);
68 int timeout = -1;
69 int read_ep = m_con.GetProbeResult().m_epModem.read;
70 int write_ep = m_con.GetProbeResult().m_epModem.write;
71 Data data;
73 m_dev.BulkWrite(write_ep, pw_start, sizeof(pw_start), timeout);
74 m_dev.BulkRead(read_ep, data, 5000);
76 // Need to add checks for other packet types.
77 // check for 02 00 00 00 SS SS SS SS RR 00 00 00 0a 00 00 00 PP PP PP PP PP 00 00 00 78 56 34 12
78 if( data.GetSize() > 4 && data.GetData()[0] == 0x02 &&
79 memcmp(data.GetData() + data.GetSize() - 4, special_flag, sizeof(special_flag))== 0 ) {
80 // Got a password request packet
81 ddout("IPModem password request packet:\n" << data);
83 // Check how many retries are left
84 if( data.GetData()[8] < 6 ) {
85 throw BadPassword("Fewer than 6 password tries "
86 "remaining in device. Refusing to proceed, "
87 "to avoid device zapping itself. Use a "
88 "Windows client, or re-cradle the device.",
89 data.GetData()[8],
90 true);
93 // Build the password hash
94 unsigned char pwdigest[SHA_DIGEST_LENGTH];
95 unsigned char prefixedhash[SHA_DIGEST_LENGTH + 4];
96 unsigned char pw_response[SHA_DIGEST_LENGTH + 8];
97 unsigned char seed[4];
99 // first, hash the password by itself
100 SHA1((unsigned char *) password, strlen(password), pwdigest);
102 // prefix the resulting hash with the provided seed
103 memcpy(&seed[0], data.GetData() + 4, sizeof(uint32_t)); // FIXME for endian support?
104 memcpy(&prefixedhash[0], &seed, sizeof(uint32_t));
105 memcpy(&prefixedhash[4], pwdigest, SHA_DIGEST_LENGTH);
107 // hash again
108 SHA1((unsigned char *) prefixedhash, SHA_DIGEST_LENGTH + 4, pwdigest);
110 // Build the response packet
111 const char pw_rsphdr[] = { 0x03, 0x00, 0x00, 0x00 };
112 memcpy(&pw_response[0], pw_rsphdr, sizeof(pw_rsphdr));
113 memcpy(&pw_response[4], pwdigest, SHA_DIGEST_LENGTH);
114 memcpy(&pw_response[24], special_flag, sizeof(special_flag));
116 // Send the password response packet
117 m_dev.BulkWrite(write_ep, pw_response, sizeof(pw_response), timeout);
118 m_dev.BulkRead(read_ep, data, 5000);
120 // check response 04 00 00 00 .......
121 // the seed is incremented, retries are reset to 10 when the password is accepted.
122 if( data.GetData()[0] == 0x04 && data.GetData()[8] == 0x0a ) {
123 ddout("IPModem password accepted.\n");
125 // send "start"? packet
126 m_dev.BulkWrite(write_ep, pw_start, sizeof(pw_start), timeout);
128 // send packet with the last 8 bytes of the password hash (session_key?)
129 //unsigned char pw_response[SHA_DIGEST_LENGTH + 8];
130 unsigned char pw_rsphdr[] = { 0x00, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0xc2, 1, 0 };
131 //unsigned char session_key[8];
132 memcpy(&session_key[0], pwdigest + 12, 8);
133 memcpy(&pw_response[0], pw_rsphdr, sizeof(pw_rsphdr));
134 memcpy(&pw_response[16], pwdigest + 12, 8);
135 //memcpy(&pw_response[16], session_key, 8);
136 memcpy(&pw_response[24], special_flag, sizeof(special_flag));
137 m_dev.BulkWrite(write_ep, pw_response, sizeof(pw_response), timeout);
139 // blank password hashes as we don't need these anymore
140 memset(pwdigest, 0, sizeof(pwdigest));
141 memset(prefixedhash, 0, sizeof(prefixedhash));
142 // The modem should be ready to accept AT commands
143 return true;
145 else { //FIXME need to clean up better....
146 // Wrong packet or invalid password
147 ddout("IPModem invalid password.\n" << data);
148 throw Barry::Error("IpModem:: Error invalid password.");
151 return false;
154 //////////////////////////////////////////////////////////////////////////////
155 // protected API / static functions
157 void *IpModem::DataReadThread(void *userptr)
159 IpModem *ipmodem = (IpModem*) userptr;
161 int read_ep = ipmodem->m_con.GetProbeResult().m_epModem.read;
162 Data data;
164 while( ipmodem->m_continue_reading ) {
166 try {
168 ipmodem->m_dev.BulkRead(read_ep, data, 5000);
170 // is it a special code?
171 if( data.GetSize() > 4 &&
172 memcmp(data.GetData() + data.GetSize() - 4, special_flag, sizeof(special_flag)) == 0 ) {
173 // log, then drop it on the floor for now
174 continue;
177 // call callback if available
178 if( ipmodem->m_callback ) {
179 (*ipmodem->m_callback)(ipmodem->m_callback_context,
180 data.GetData(),
181 data.GetSize());
183 // else {
184 // // append data to readCache
185 // FIXME;
186 // }
189 catch( Usb::Timeout &to ) {
190 // do nothing on timeouts
191 ddout("Timeout in DataReadThread!");
193 catch( std::exception &e ) {
194 eout("Exception in IpModem::DataReadThread: " << e.what());
196 catch( ... ) {
197 eout("Unknown exception in IpModem::DataReadThread, ignoring!");
201 return 0;
204 //////////////////////////////////////////////////////////////////////////////
205 // public API
207 void IpModem::Open(const char *password)
209 // check that we have endpoints for the modem
210 const Usb::EndpointPair &pair = m_con.GetProbeResult().m_epModem;
211 if( !pair.IsComplete() ) {
212 std::ostringstream oss;
213 oss << "IP Modem not supported by this device: "
214 << "read: " << std::hex << (unsigned int) pair.read
215 << " write: " << std::hex << (unsigned int) pair.write
216 << " type: " << std::hex << (unsigned int) pair.type;
217 eout(oss.str());
218 throw Barry::Error(oss.str());
221 // clear halt when starting out
222 m_dev.ClearHalt(pair.read);
223 m_dev.ClearHalt(pair.write);
225 if( strlen(password) != 0 ) {
226 if( !SendPassword(password) ) {
227 throw Barry::Error("IpModem:: Error sending password.");
230 else {
231 Data block(start, sizeof(start));
232 Write(block);
235 // spawn read thread
236 m_continue_reading = true;
237 int ret = pthread_create(&m_modem_read_thread, NULL, &IpModem::DataReadThread, this);
238 if( ret ) {
239 m_continue_reading = false;
240 throw Barry::ErrnoError("IpModem:: Error creating USB read thread.", ret);
244 void IpModem::Write(const Data &data, int timeout)
246 if( data.GetSize() == 0 )
247 return; // nothing to do
249 // m_dev.ClearHalt(m_con.GetProbeResult().m_epModem.write);
251 m_dev.BulkWrite(m_con.GetProbeResult().m_epModem.write,
252 m_filter.Write(data), timeout);
255 void IpModem::Close()
257 // This is the terminate connection sequence
258 // that resets the modem so we can re-connect
259 // without unpluging the USB cable or reseting
260 // the whole device.
261 // This works on a BB 8703e a with password. other BB's??
262 unsigned char end[28];
263 int timeout = -1;
264 int read_ep = m_con.GetProbeResult().m_epModem.read;
265 int write_ep = m_con.GetProbeResult().m_epModem.write;
266 Data data;
268 ddout("IpModem:: Closing connection.");
270 // Wait a bit for the modem to stop sending data
271 // before stopping the read thread.
272 sleep(4);
274 // stop the read thread
275 if( m_continue_reading ) {
276 m_continue_reading = false;
277 pthread_join(m_modem_read_thread, NULL);
280 //0 0 0 0 b0 0 0 0 0 0 0 0 0 c2 1 0 + session_key + special_flag
281 memset(end, 0, sizeof(end));
282 end[4] = 0xb0;
283 end[13] = 0xc2;
284 end[14] = 0x01;
285 memcpy(&end[16], session_key, sizeof(session_key));
286 memcpy(&end[24], special_flag, sizeof(special_flag));
287 m_dev.BulkWrite(write_ep, end, sizeof(end), timeout);
289 //FIXME move try-catch-read to a drainRead function
290 // since we're just throwing the packets away
291 try {
292 m_dev.BulkRead(read_ep, data, 5000);
293 ddout("IPModem read packet:\n" << data);
295 catch( Usb::Timeout &to ) {
296 // do nothing on timeouts
297 ddout("Timeout in DataReadThread!");
300 //0 0 0 0 20 0 0 0 3 0 0 0 0 c2 1 0 + session_key + special_flag
301 memset(end, 0, sizeof(end));
302 end[4] = 0x20;
303 end[8] = 0x03;
304 end[13] = 0xc2;
305 end[14] = 0x01;
306 memcpy(&end[16], session_key, sizeof(session_key));
307 memcpy(&end[24], special_flag, sizeof(special_flag));
308 m_dev.BulkWrite(write_ep, end, sizeof(end), timeout);
309 try {
310 m_dev.BulkRead(read_ep, data, 5000);
311 ddout("IPModem read packet:\n" << data);
313 catch( Usb::Timeout &to ) {
314 // do nothing on timeouts
315 ddout("Timeout in DataReadThread!");
318 //0 0 0 0 30 0 0 0 0 0 0 0 0 c2 1 0 + session_key + special_flag
319 memset(end, 0, sizeof(end));
320 end[4] = 0x30;
321 end[13] = 0xc2;
322 end[14] = 0x01;
323 memcpy(&end[16], session_key, sizeof(session_key));
324 memcpy(&end[24], special_flag, sizeof(special_flag));
325 m_dev.BulkWrite(write_ep, end, sizeof(end), timeout);
326 try {
327 m_dev.BulkRead(read_ep, data, 5000);
328 ddout("IPModem read packet:\n" << data);
330 catch( Usb::Timeout &to ) {
331 // do nothing on timeouts
332 ddout("Timeout in DataReadThread!");
335 const char stop[] = { 0x01, 0, 0, 0, 0, 0, 0, 0, 0x78, 0x56, 0x34, 0x12 };
336 m_dev.BulkWrite(write_ep, stop, sizeof(stop), timeout);
337 try {
338 m_dev.BulkRead(read_ep, data, 5000);
339 ddout("IPModem read packet:\n" << data);
341 catch( Usb::Timeout &to ) {
342 // do nothing on timeouts
343 ddout("Timeout in DataReadThread!");
348 }} // namespace Barry::Mode