Reduced minimum password retry level from 6 to 3
[barry/pauldeden.git] / src / m_ipmodem.cc
blob8d5344358d9e257b3d3997bd78558e0e4ccaea23
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 "sha1.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 };
36 const char stop[] = { 0x01, 0, 0, 0, 0, 0, 0, 0, 0x78, 0x56, 0x34, 0x12 };
38 //////////////////////////////////////////////////////////////////////////////
39 // Mode::IpModem class
41 IpModem::IpModem(Controller &con,
42 DeviceDataCallback callback,
43 void *callback_context)
44 : m_con(con)
45 , m_dev(con.m_dev)
46 , m_continue_reading(false)
47 , m_callback(callback)
48 , m_callback_context(callback_context)
50 memset(m_session_key, 0, sizeof(m_session_key));
53 IpModem::~IpModem()
55 try {
56 Close();
57 } catch( std::exception &e ) {
58 dout("Exception caught in IpModem destructor, ignoring: "
59 << e.what());
63 bool IpModem::SendPassword(const char *password)
65 if( !password || strlen(password) == 0 ) {
66 throw BadPassword("No password provided.", 0, false);
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));
74 m_dev.BulkRead(read_ep, data);
75 ddout("IPModem read packet.\n" << data);
77 // Need to add checks for other packet types.
78 // 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
79 if( data.GetSize() >= 9 && data.GetData()[0] == 0x02 &&
80 memcmp(data.GetData() + data.GetSize() - 4, special_flag, sizeof(special_flag))== 0 ) {
81 // Got a password request packet
82 ddout("IPModem password request packet:\n" << data);
84 // Check how many retries are left
85 if( data.GetData()[8] < BARRY_MIN_PASSWORD_TRIES ) {
86 throw BadPassword("Fewer than " BARRY_MIN_PASSWORD_TRIES_ASC " password tries remaining in device. Refusing to proceed, to avoid device zapping itself. Use a Windows client, or re-cradle the device.",
87 data.GetData()[8],
88 true);
91 // Build the password hash
92 unsigned char pwdigest[SHA_DIGEST_LENGTH];
93 unsigned char prefixedhash[SHA_DIGEST_LENGTH + 4];
94 unsigned char pw_response[SHA_DIGEST_LENGTH + 8];
95 unsigned char seed[4];
97 // first, hash the password by itself
98 SHA1((unsigned char *) password, strlen(password), pwdigest);
100 // prefix the resulting hash with the provided seed
101 memcpy(&seed[0], data.GetData() + 4, sizeof(uint32_t));
102 memcpy(&prefixedhash[0], &seed, sizeof(uint32_t));
103 memcpy(&prefixedhash[4], pwdigest, SHA_DIGEST_LENGTH);
105 // hash again
106 SHA1((unsigned char *) prefixedhash, SHA_DIGEST_LENGTH + 4, pwdigest);
108 // Build the response packet
109 const char pw_rsphdr[] = { 0x03, 0x00, 0x00, 0x00 };
110 memcpy(&pw_response[0], pw_rsphdr, sizeof(pw_rsphdr));
111 memcpy(&pw_response[4], pwdigest, SHA_DIGEST_LENGTH);
112 memcpy(&pw_response[24], special_flag, sizeof(special_flag));
114 // Send the password response packet
115 m_dev.BulkWrite(write_ep, pw_response, sizeof(pw_response));
116 m_dev.BulkRead(read_ep, data);
117 ddout("IPModem read password response.\n" << data);
119 // check response 04 00 00 00 .......
120 // On the 8703e the seed is incremented, retries are reset to 10 when the password is accepted.
121 // if( data.GetData()[0] == 0x04 && data.GetData()[8] == 0x0a ) {
122 if( data.GetSize() >= 9 && data.GetData()[0] == 0x04 ) {
123 if( memcmp(data.GetData() + 4, seed, sizeof(seed)) == 0 ) {
124 ddout("IPModem invalid password.\n" << data);
125 throw BadPassword("Password rejected by device.", data.GetData()[8], false);
127 ddout("IPModem password accepted.\n");
128 // send "start"? packet
129 m_dev.BulkWrite(write_ep, pw_start, sizeof(pw_start));
131 // send packet with the last 8 bytes of the password hash (session_key?)
132 //unsigned char pw_response[SHA_DIGEST_LENGTH + 8];
133 unsigned char pw_rsphdr[] = { 0x00, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0xc2, 1, 0 };
134 memcpy(&m_session_key[0], pwdigest + 12, sizeof(m_session_key));
135 memcpy(&pw_response[0], pw_rsphdr, sizeof(pw_rsphdr));
136 memcpy(&pw_response[16], pwdigest + 12, 8);
137 //memcpy(&pw_response[16], m_session_key, 8);
138 memcpy(&pw_response[24], special_flag, sizeof(special_flag));
139 m_dev.BulkWrite(write_ep, pw_response, sizeof(pw_response));
141 // blank password hashes as we don't need these anymore
142 memset(pwdigest, 0, sizeof(pwdigest));
143 memset(prefixedhash, 0, sizeof(prefixedhash));
145 // The modem should be ready to accept AT commands
146 return true;
149 // Unknown packet
150 ddout("IPModem Error unknown packet.\n" << data);
152 return false;
155 //////////////////////////////////////////////////////////////////////////////
156 // protected API / static functions
158 void *IpModem::DataReadThread(void *userptr)
160 IpModem *ipmodem = (IpModem*) userptr;
162 int read_ep = ipmodem->m_con.GetProbeResult().m_epModem.read;
163 Data data;
165 while( ipmodem->m_continue_reading ) {
167 try {
169 ipmodem->m_dev.BulkRead(read_ep, data, 5000);
171 // is it a special code?
172 if( data.GetSize() > 4 &&
173 memcmp(data.GetData() + data.GetSize() - 4, special_flag, sizeof(special_flag)) == 0 ) {
174 // log, then drop it on the floor for now
175 ddout("IPModem special packet:\n" << data);
176 continue;
179 // call callback if available
180 if( ipmodem->m_callback ) {
181 (*ipmodem->m_callback)(ipmodem->m_callback_context,
182 data.GetData(),
183 data.GetSize());
185 // else {
186 // // append data to readCache
187 // FIXME;
188 // }
191 catch( Usb::Timeout &to ) {
192 // do nothing on timeouts
193 ddout("Timeout in DataReadThread!");
195 catch( std::exception &e ) {
196 eout("Exception in IpModem::DataReadThread: " << e.what());
200 return 0;
203 //////////////////////////////////////////////////////////////////////////////
204 // public API
206 void IpModem::Open(const char *password)
208 int read_ep = m_con.GetProbeResult().m_epModem.read;
209 int write_ep = m_con.GetProbeResult().m_epModem.write;
210 Data data;
211 // check that we have endpoints for the modem
212 const Usb::EndpointPair &pair = m_con.GetProbeResult().m_epModem;
213 if( !pair.IsComplete() ) {
214 std::ostringstream oss;
215 oss << "IP Modem not supported by this device: "
216 << "read: " << std::hex << (unsigned int) pair.read
217 << " write: " << std::hex << (unsigned int) pair.write
218 << " type: " << std::hex << (unsigned int) pair.type;
219 eout(oss.str());
220 throw Barry::Error(oss.str());
223 // clear halt when starting out
224 m_dev.ClearHalt(pair.read);
225 m_dev.ClearHalt(pair.write);
227 if( !password || strlen(password) == 0 ) {
228 Data block(start, sizeof(start));
229 Write(block);
231 else {
232 if( !SendPassword(password) ) {
233 throw Barry::Error("IpModem:: Error sending password.");
237 // see if the modem will respond to commands
238 const char modem_command[] = { "AT\r" };
239 m_dev.BulkWrite(write_ep, modem_command, sizeof(modem_command));
240 m_dev.BulkRead(read_ep, data);
241 ddout("IPModem:: AT command response.\n" << data);
242 if( data.GetSize() >= 1 ) {
243 switch(data.GetData()[0])
245 case 0x02: // password seed received
246 throw BadPassword("This device requested a password.",
247 data.GetSize() >= 9 ? data.GetData()[8] : 0, false);
249 case 0x04: // command accepted
250 break;
252 case 0x07: // device is password protected?
253 throw BadPassword("This device requires a password.", 0, false);
255 default: // ???
256 ddout("IPModem:: unknown AT command response.\n");
257 break;
261 // spawn read thread
262 m_continue_reading = true;
263 int ret = pthread_create(&m_modem_read_thread, NULL, &IpModem::DataReadThread, this);
264 if( ret ) {
265 m_continue_reading = false;
266 throw Barry::ErrnoError("IpModem:: Error creating USB read thread.", ret);
270 void IpModem::Write(const Data &data, int timeout)
272 if( data.GetSize() == 0 )
273 return; // nothing to do
275 // according to Rick Scott the m_filter is not needed with the ip modem
276 // but with the 8320 with Rogers, it doesn't seem to connect without it
277 // If this is a performance problem, perhaps make this a runtime
278 // option.
279 m_dev.BulkWrite(m_con.GetProbeResult().m_epModem.write,
280 m_filter.Write(data), timeout);
283 void IpModem::Close()
285 // This is the terminate connection sequence
286 // that resets the modem so we can re-connect
287 // without unpluging the USB cable or reseting
288 // the whole device.
289 // This works on a BB 8703e a with password. other BB's??
290 unsigned char end[28];
291 int read_ep = m_con.GetProbeResult().m_epModem.read;
292 int write_ep = m_con.GetProbeResult().m_epModem.write;
293 Data data;
295 //0 0 0 0 b0 0 0 0 0 0 0 0 0 c2 1 0 + session_key + special_flag
296 ddout("IpModem:: Closing connection.");
297 memset(end, 0, sizeof(end));
298 end[4] = 0xb0;
299 end[13] = 0xc2;
300 end[14] = 0x01;
301 memcpy(&end[16], m_session_key, sizeof(m_session_key));
302 memcpy(&end[24], special_flag, sizeof(special_flag));
303 m_dev.BulkWrite(write_ep, end, sizeof(end));
305 //0 0 0 0 20 0 0 0 3 0 0 0 0 c2 1 0 + session_key + special_flag
306 memset(end, 0, sizeof(end));
307 end[4] = 0x20;
308 end[8] = 0x03;
309 end[13] = 0xc2;
310 end[14] = 0x01;
311 memcpy(&end[16], m_session_key, sizeof(m_session_key));
312 memcpy(&end[24], special_flag, sizeof(special_flag));
313 m_dev.BulkWrite(write_ep, end, sizeof(end));
315 //0 0 0 0 30 0 0 0 0 0 0 0 0 c2 1 0 + session_key + special_flag
316 // The session_key is set to 0x0's when there is no password.
317 memset(end, 0, sizeof(end));
318 end[4] = 0x30;
319 end[13] = 0xc2;
320 end[14] = 0x01;
321 memcpy(&end[16], m_session_key, sizeof(m_session_key));
322 memcpy(&end[24], special_flag, sizeof(special_flag));
323 m_dev.BulkWrite(write_ep, end, sizeof(end));
324 m_dev.BulkWrite(write_ep, stop, sizeof(stop));
325 try {
326 m_dev.BulkRead(read_ep, data, 5000);
327 ddout("IPModem:: Close read packet:\n" << data);
329 catch( Usb::Timeout &to ) {
330 // do nothing on timeouts
331 ddout("IPModem:: Close Read Timeout");
333 // stop the read thread
334 if( m_continue_reading ) {
335 m_continue_reading = false;
336 pthread_join(m_modem_read_thread, NULL);
338 ddout("IPmodem:: Closed!");
342 }} // namespace Barry::Mode