1 /*
2 OWFS -- One-Wire filesystem
3 OWHTTPD -- One-Wire Web Server
4 Written 2003 Paul H Alfille
5 email: paul.alfille@gmail.com
6 Released under the GPL
7 See the header file: ow.h for full attribution
8 1wire/iButton system from Dallas Semiconductor
9 */
10
11 /* General Device File format:
12 This device file corresponds to a specific 1wire/iButton chip type
13 ( or a closely related family of chips )
14
15 The connection to the larger program is through the "device" data structure,
16 which must be declared in the acompanying header file.
17
18 The device structure holds the
19 family code,
20 name,
21 device type (chip, interface or pseudo)
22 number of properties,
23 list of property structures, called "filetype".
24
25 Each filetype structure holds the
26 name,
27 estimated length (in bytes),
28 aggregate structure pointer,
29 data format,
30 read function,
31 write funtion,
32 generic data pointer
33
34 The aggregate structure, is present for properties that several members
35 (e.g. pages of memory or entries in a temperature log. It holds:
36 number of elements
37 whether the members are lettered or numbered
38 whether the elements are stored together and split, or separately and joined
39 */
40
41 #include <config.h>
42 #include "owfs_config.h"
43 #include "ow_1991.h"
44
45 /* ------- Prototypes ----------- */
46
47 WRITE_FUNCTION(FS_password);
48 WRITE_FUNCTION(FS_reset);
49 READ_FUNCTION(FS_r_subkey);
50 WRITE_FUNCTION(FS_w_subkey);
51 READ_FUNCTION(FS_r_id);
52 WRITE_FUNCTION(FS_w_id);
53
54 #define _DS1991_PAGES 3
55 #define _DS1991_PAGE_LENGTH 0x40
56
57 #define _DS1991_ID_START 0x00
58 #define _DS1991_ID_LENGTH 8
59
60 #define _DS1991_PASSWORD_START (_DS1991_ID_START + _DS1991_ID_LENGTH )
61 #define _DS1991_PASSWORD_LENGTH 8
62
63 #define _DS1991_DATA_START ( _DS1991_PASSWORD_START + _DS1991_PASSWORD_LENGTH )
64 #define _DS1991_DATA_LENGTH (_DS1991_PAGE_LENGTH - _DS1991_DATA_START)
65
66
67 BYTE subkey_byte[3] = { (0<<6), (1<<6), (2<<6), } ;
68
69
70 /* ------- Structures ----------- */
71
72 static struct aggregate A1991_password = { 0, ag_letters, ag_sparse, };
73 static struct filetype DS1991[] = {
74 F_STANDARD,
75
76 {"subkey0", PROPERTY_LENGTH_SUBDIR, NON_AGGREGATE, ft_subdir, fc_subdir, NO_READ_FUNCTION, NO_WRITE_FUNCTION, VISIBLE, NO_FILETYPE_DATA, },
77 {"subkey0/password", _DS1991_ID_LENGTH, &A1991_password, ft_binary, fc_stable, NO_READ_FUNCTION, FS_password, VISIBLE, {.i=0,}, },
78 {"subkey0/reset", PROPERTY_LENGTH_YESNO, &A1991_password, ft_yesno, fc_stable, NO_READ_FUNCTION, FS_reset, VISIBLE, {.i=0,}, },
79 {"subkey0/secure_data", _DS1991_DATA_LENGTH, &A1991_password, ft_binary, fc_stable, FS_r_subkey, FS_w_subkey, VISIBLE, {.i=0,}, },
80 {"subkey0/id", _DS1991_ID_LENGTH, &A1991_password, ft_binary, fc_stable, FS_r_id, FS_w_id, VISIBLE, {.i=0,}, },
81
82 {"subkey1", PROPERTY_LENGTH_SUBDIR, NON_AGGREGATE, ft_subdir, fc_subdir, NO_READ_FUNCTION, NO_WRITE_FUNCTION, VISIBLE, NO_FILETYPE_DATA, },
83 {"subkey1/password", _DS1991_ID_LENGTH, &A1991_password, ft_binary, fc_stable, NO_READ_FUNCTION, FS_password, VISIBLE, {.i=1,}, },
84 {"subkey1/reset", PROPERTY_LENGTH_YESNO, &A1991_password, ft_yesno, fc_stable, NO_READ_FUNCTION, FS_reset, VISIBLE, {.i=1,}, },
85 {"subkey1/secure_data", _DS1991_DATA_LENGTH, &A1991_password, ft_binary, fc_stable, FS_r_subkey, FS_w_subkey, VISIBLE, {.i=1,}, },
86 {"subkey1/id", _DS1991_ID_LENGTH, &A1991_password, ft_binary, fc_stable, FS_r_id, FS_w_id, VISIBLE, {.i=1,}, },
87
88 {"subkey2", PROPERTY_LENGTH_SUBDIR, NON_AGGREGATE, ft_subdir, fc_subdir, NO_READ_FUNCTION, NO_WRITE_FUNCTION, VISIBLE, NO_FILETYPE_DATA, },
89 {"subkey2/password", _DS1991_ID_LENGTH, &A1991_password, ft_binary, fc_stable, NO_READ_FUNCTION, FS_password, VISIBLE, {.i=2,}, },
90 {"subkey2/reset", PROPERTY_LENGTH_YESNO, &A1991_password, ft_yesno, fc_stable, NO_READ_FUNCTION, FS_reset, VISIBLE, {.i=2,}, },
91 {"subkey2/secure_data", _DS1991_DATA_LENGTH, &A1991_password, ft_binary, fc_stable, FS_r_subkey, FS_w_subkey, VISIBLE, {.i=2,}, },
92 {"subkey2/id", _DS1991_ID_LENGTH, &A1991_password, ft_binary, fc_stable, FS_r_id, FS_w_id, VISIBLE, {.i=2,}, },
93 };
94
95 DeviceEntry(02, DS1991, NO_GENERIC_READ, NO_GENERIC_WRITE);
96
97 static struct filetype DS1425[] = {
98 F_STANDARD,
99
100 {"subkey0", PROPERTY_LENGTH_SUBDIR, NON_AGGREGATE, ft_subdir, fc_subdir, NO_READ_FUNCTION, NO_WRITE_FUNCTION, VISIBLE, NO_FILETYPE_DATA, },
101 {"subkey0/password", _DS1991_ID_LENGTH, &A1991_password, ft_binary, fc_stable, NO_READ_FUNCTION, FS_password, VISIBLE, {.i=0,}, },
102 {"subkey0/reset", PROPERTY_LENGTH_YESNO, &A1991_password, ft_yesno, fc_stable, NO_READ_FUNCTION, FS_reset, VISIBLE, {.i=0,}, },
103 {"subkey0/secure_data", _DS1991_DATA_LENGTH, &A1991_password, ft_binary, fc_stable, FS_r_subkey, FS_w_subkey, VISIBLE, {.i=0,}, },
104 {"subkey0/id", _DS1991_ID_LENGTH, &A1991_password, ft_binary, fc_stable, FS_r_id, FS_w_id, VISIBLE, {.i=0,}, },
105
106 {"subkey1", PROPERTY_LENGTH_SUBDIR, NON_AGGREGATE, ft_subdir, fc_subdir, NO_READ_FUNCTION, NO_WRITE_FUNCTION, VISIBLE, NO_FILETYPE_DATA, },
107 {"subkey1/password", _DS1991_ID_LENGTH, &A1991_password, ft_binary, fc_stable, NO_READ_FUNCTION, FS_password, VISIBLE, {.i=1,}, },
108 {"subkey1/reset", PROPERTY_LENGTH_YESNO, &A1991_password, ft_yesno, fc_stable, NO_READ_FUNCTION, FS_reset, VISIBLE, {.i=1,}, },
109 {"subkey1/secure_data", _DS1991_DATA_LENGTH, &A1991_password, ft_binary, fc_stable, FS_r_subkey, FS_w_subkey, VISIBLE, {.i=1,}, },
110 {"subkey1/id", _DS1991_ID_LENGTH, &A1991_password, ft_binary, fc_stable, FS_r_id, FS_w_id, VISIBLE, {.i=1,}, },
111
112 {"subkey2", PROPERTY_LENGTH_SUBDIR, NON_AGGREGATE, ft_subdir, fc_subdir, NO_READ_FUNCTION, NO_WRITE_FUNCTION, VISIBLE, NO_FILETYPE_DATA, },
113 {"subkey2/password", _DS1991_ID_LENGTH, &A1991_password, ft_binary, fc_stable, NO_READ_FUNCTION, FS_password, VISIBLE, {.i=2,}, },
114 {"subkey2/reset", PROPERTY_LENGTH_YESNO, &A1991_password, ft_yesno, fc_stable, NO_READ_FUNCTION, FS_reset, VISIBLE, {.i=2,}, },
115 {"subkey2/secure_data", _DS1991_DATA_LENGTH, &A1991_password, ft_binary, fc_stable, FS_r_subkey, FS_w_subkey, VISIBLE, {.i=2,}, },
116 {"subkey2/id", _DS1991_ID_LENGTH, &A1991_password, ft_binary, fc_stable, FS_r_id, FS_w_id, VISIBLE, {.i=2,}, },
117 };
118
119 DeviceEntry(82, DS1425, NO_GENERIC_READ, NO_GENERIC_WRITE);
120
121 #define _1W_WRITE_SCRATCHPAD 0x96
122 #define _1W_READ_SCRATCHPAD 0x69
123 #define _1W_COPY_SCRATCHPAD 0x3C
124 #define _1W_WRITE_PASSWORD 0x5A
125 #define _1W_WRITE_SUBKEY 0x99
126 #define _1W_READ_SUBKEY 0x66
127
128 /* ------- Functions ------------ */
129
130
131 static GOOD_OR_BAD OW_password( int subkey, const BYTE * new_id, const BYTE * new_password, const struct parsedname * pn ) ;
132 static GOOD_OR_BAD OW_read_subkey( int subkey, BYTE * password, size_t size, off_t offset, BYTE * data, const struct parsedname * pn ) ;
133 static GOOD_OR_BAD OW_write_subkey( int subkey, BYTE * password, size_t size, off_t offset, BYTE * data, const struct parsedname * pn ) ;
134 static GOOD_OR_BAD OW_read_id( int subkey, BYTE * id, const struct parsedname * pn ) ;
135 static GOOD_OR_BAD OW_write_id( int subkey, BYTE * password, BYTE * id, const struct parsedname * pn ) ;
136 static GOOD_OR_BAD OW_write_password( int subkey, BYTE * old_password, BYTE * new_password, const struct parsedname * pn ) ;
137
138 static GOOD_OR_BAD ToPassword( char * text, BYTE * psw ) ;
139
140
141 /* array with magic bytes representing the Copy Scratch operations */
142 enum block { block_ALL = 0, block_IDENT, block_PASSWORD, block_DATA };
143 #define _DS1991_COPY_BLOCK_LENGTH 8
144 #define _DS1991_COPY_BLOCK_ITEMS 9
145
146 static const BYTE cp_array[_DS1991_COPY_BLOCK_ITEMS][_DS1991_COPY_BLOCK_LENGTH] = {
147 {0x56, 0x56, 0x7F, 0x51, 0x57, 0x5D, 0x5A, 0x7F}, // 00-3F
148 {0x9A, 0x9A, 0xB3, 0x9D, 0x64, 0x6E, 0x69, 0x4C}, // ident
149 {0x9A, 0x9A, 0x4C, 0x62, 0x9B, 0x91, 0x69, 0x4C}, // passwd
150 {0x9A, 0x65, 0xB3, 0x62, 0x9B, 0x6E, 0x96, 0x4C}, // 10-17
151 {0x6A, 0x6A, 0x43, 0x6D, 0x6B, 0x61, 0x66, 0x43}, // 18-1F
152 {0x95, 0x95, 0xBC, 0x92, 0x94, 0x9E, 0x99, 0xBC}, // 20-27
153 {0x65, 0x9A, 0x4C, 0x9D, 0x64, 0x91, 0x69, 0xB3}, // 28-2F
154 {0x65, 0x65, 0xB3, 0x9D, 0x64, 0x6E, 0x96, 0xB3}, // 30-37
155 {0x65, 0x65, 0x4C, 0x62, 0x9B, 0x91, 0x96, 0xB3}, // 38-3F
156 };
157
158 #ifndef MIN
159 #define MIN(a, b) ((a) < (b) ? (a) : (b))
160 #endif
161
162 // reset the subkey area with a new password
163 // clear all data
164 // put default ID in it.
FS_reset(struct one_wire_query * owq)165 static ZERO_OR_ERROR FS_reset(struct one_wire_query *owq)
166 {
167 struct parsedname *pn = PN(owq);
168 int subkey = pn->selected_filetype->data.i ;
169 BYTE new_password[_DS1991_PASSWORD_LENGTH] ;
170 BYTE new_id[_DS1991_PASSWORD_LENGTH + 1 ] ;
171
172 if ( OWQ_Y(owq) == 0 ) {
173 // Not true request
174 return 0 ;
175 }
176
177 if ( BAD(ToPassword( pn->sparse_name, new_password ) ) ) {
178 return -EINVAL ;
179 }
180
181 snprintf( (char *) new_id, _DS1991_ID_LENGTH + 1, "Subkey %.1d", subkey ) ;
182
183 return GB_to_Z_OR_E(OW_password(subkey, new_id, new_password, pn)) ;
184 }
185
186 // write a new password without clearing data
187 // done with a scratchpad copy
FS_password(struct one_wire_query * owq)188 static ZERO_OR_ERROR FS_password(struct one_wire_query *owq)
189 {
190 struct parsedname *pn = PN(owq);
191 int subkey = pn->selected_filetype->data.i ;
192 BYTE new_password[_DS1991_PASSWORD_LENGTH] ;
193 BYTE old_password[_DS1991_PASSWORD_LENGTH] ;
194
195 if ( OWQ_offset(owq) != 0 ) {
196 return -EINVAL ;
197 }
198
199 if ( OWQ_size(owq) != _DS1991_PASSWORD_LENGTH ) {
200 return -EINVAL ;
201 }
202
203 memcpy( new_password, (BYTE *)OWQ_buffer(owq) , _DS1991_PASSWORD_LENGTH ) ;
204 if ( BAD(ToPassword( pn->sparse_name, old_password ) ) ) {
205 return -EINVAL ;
206 }
207
208 return GB_to_Z_OR_E(OW_write_password(subkey, old_password, new_password, pn)) ;
209 }
210
211 // write to secure data area
212 // needs the password encoded in the extension
FS_w_subkey(struct one_wire_query * owq)213 static ZERO_OR_ERROR FS_w_subkey(struct one_wire_query *owq)
214 {
215 struct parsedname *pn = PN(owq);
216 int subkey = pn->selected_filetype->data.i ;
217 BYTE password[_DS1991_PASSWORD_LENGTH] ;
218
219 if ( BAD(ToPassword( pn->sparse_name, password ) ) ) {
220 return -EINVAL ;
221 }
222
223 if ( BAD (OW_write_subkey( subkey, password, OWQ_size(owq), OWQ_offset(owq)+_DS1991_DATA_START, (BYTE *) OWQ_buffer(owq), pn )) ) {
224 return -EINVAL ;
225 }
226
227 return 0 ;
228 }
229
230 // read from secure data area
231 // needs the password encoded in the extension
FS_r_subkey(struct one_wire_query * owq)232 static ZERO_OR_ERROR FS_r_subkey(struct one_wire_query *owq)
233 {
234 struct parsedname *pn = PN(owq);
235 int subkey = pn->selected_filetype->data.i ;
236 BYTE password[_DS1991_PASSWORD_LENGTH] ;
237
238 if ( BAD(ToPassword( pn->sparse_name, password ) ) ) {
239 return -EINVAL ;
240 }
241
242 if ( BAD (OW_read_subkey( subkey, password, OWQ_size(owq), OWQ_offset(owq)+_DS1991_DATA_START, (BYTE *) OWQ_buffer(owq), pn )) ) {
243 return -EINVAL ;
244 }
245 OWQ_length(owq) = OWQ_size(owq) ;
246
247 return 0 ;
248 }
249
250 // read id
251 // needs a dummy password encoded in the extension
FS_r_id(struct one_wire_query * owq)252 static ZERO_OR_ERROR FS_r_id(struct one_wire_query *owq)
253 {
254 struct parsedname *pn = PN(owq);
255 int subkey = pn->selected_filetype->data.i ;
256 BYTE id[_DS1991_ID_LENGTH] ;
257
258 if ( BAD (OW_read_id( subkey, id, pn )) ) {
259 return -EINVAL ;
260 }
261
262 return OWQ_format_output_offset_and_size( (const char *) id, _DS1991_ID_LENGTH, owq ) ;
263 }
264
265 // write new id
266 // needs the password encoded in the extension
FS_w_id(struct one_wire_query * owq)267 static ZERO_OR_ERROR FS_w_id(struct one_wire_query *owq)
268 {
269 struct parsedname *pn = PN(owq);
270 int subkey = pn->selected_filetype->data.i ;
271 BYTE password[_DS1991_PASSWORD_LENGTH] ;
272 BYTE id[_DS1991_ID_LENGTH] ;
273
274 if ( BAD(ToPassword( pn->sparse_name, password ) ) ) {
275 return -EINVAL ;
276 }
277
278 if ( OWQ_offset(owq) != 0 && OWQ_size(owq) != _DS1991_ID_LENGTH ) {
279 // Fill id with existing id
280 if ( BAD (OW_read_id( subkey, id, pn )) ) {
281 return -EINVAL ;
282 }
283 }
284
285 memcpy( &id[OWQ_offset(owq)], OWQ_buffer(owq), OWQ_size(owq) ) ;
286
287 if ( BAD (OW_write_id( subkey, password, id, pn )) ) {
288 return -EINVAL ;
289 }
290
291 return 0 ;
292 }
293
OW_password(int subkey,const BYTE * new_id,const BYTE * new_password,const struct parsedname * pn)294 static GOOD_OR_BAD OW_password( int subkey, const BYTE * new_id, const BYTE * new_password, const struct parsedname * pn )
295 {
296 BYTE subkey_addr = subkey_byte[ subkey ] ;
297 BYTE write_pwd[] = { _1W_WRITE_PASSWORD, subkey_addr, BYTE_INVERSE(subkey_addr), } ;
298 BYTE old_id[_DS1991_ID_LENGTH] ;
299 struct transaction_log t[] = {
300 TRXN_START,
301 TRXN_WRITE3(write_pwd),
302 TRXN_READ(old_id, _DS1991_ID_LENGTH),
303 TRXN_WRITE(old_id, _DS1991_ID_LENGTH),
304 TRXN_WRITE(new_id, _DS1991_ID_LENGTH),
305 TRXN_WRITE(new_password, _DS1991_PASSWORD_LENGTH),
306 TRXN_END,
307 };
308
309 return BUS_transaction(t, pn) ;
310 }
311
312
OW_read_id(int subkey,BYTE * id,const struct parsedname * pn)313 static GOOD_OR_BAD OW_read_id( int subkey, BYTE * id, const struct parsedname * pn )
314 {
315 BYTE subkey_addr = subkey_byte[ subkey ] + _DS1991_DATA_START ;
316 BYTE read_sbk[] = { _1W_READ_SUBKEY, subkey_addr, BYTE_INVERSE(subkey_addr), } ;
317 struct transaction_log t[] = {
318 TRXN_START,
319 TRXN_WRITE3(read_sbk),
320 TRXN_READ(id, _DS1991_ID_LENGTH),
321 TRXN_END,
322 };
323
324 return BUS_transaction(t, pn) ;
325 }
326
OW_read_subkey(int subkey,BYTE * password,size_t size,off_t offset,BYTE * data,const struct parsedname * pn)327 static GOOD_OR_BAD OW_read_subkey( int subkey, BYTE * password, size_t size, off_t offset, BYTE * data, const struct parsedname * pn )
328 {
329 BYTE subkey_addr = subkey_byte[ subkey ] + offset ;
330 BYTE write_sbk[] = { _1W_READ_SUBKEY, subkey_addr, BYTE_INVERSE(subkey_addr), } ;
331 BYTE old_id[_DS1991_ID_LENGTH] ;
332 struct transaction_log t[] = {
333 TRXN_START,
334 TRXN_WRITE3(write_sbk),
335 TRXN_READ(old_id, _DS1991_ID_LENGTH),
336 TRXN_WRITE(password, _DS1991_PASSWORD_LENGTH),
337 TRXN_READ(data, size),
338 TRXN_END,
339 };
340
341 return BUS_transaction(t, pn) ;
342 }
343
OW_write_subkey(int subkey,BYTE * password,size_t size,off_t offset,BYTE * data,const struct parsedname * pn)344 static GOOD_OR_BAD OW_write_subkey( int subkey, BYTE * password, size_t size, off_t offset, BYTE * data, const struct parsedname * pn )
345 {
346 BYTE subkey_addr = subkey_byte[ subkey ] + offset ;
347 BYTE write_sbk[] = { _1W_WRITE_SUBKEY, subkey_addr, BYTE_INVERSE(subkey_addr), } ;
348 BYTE old_id[_DS1991_ID_LENGTH] ;
349 struct transaction_log t[] = {
350 TRXN_START,
351 TRXN_WRITE3(write_sbk),
352 TRXN_READ(old_id, _DS1991_ID_LENGTH),
353 TRXN_WRITE(password, _DS1991_PASSWORD_LENGTH),
354 TRXN_WRITE(data, size),
355 TRXN_END,
356 };
357
358 return BUS_transaction(t, pn) ;
359 }
360
OW_write_id(int subkey,BYTE * password,BYTE * id,const struct parsedname * pn)361 static GOOD_OR_BAD OW_write_id( int subkey, BYTE * password, BYTE * id, const struct parsedname * pn )
362 {
363 BYTE scratch_addr = (3<<6) + _DS1991_ID_START ;
364 BYTE write_scratch[] = { _1W_WRITE_SCRATCHPAD, scratch_addr, BYTE_INVERSE(scratch_addr), } ;
365 struct transaction_log tcopy[] = {
366 TRXN_START,
367 TRXN_WRITE3(write_scratch),
368 TRXN_WRITE(id, _DS1991_ID_LENGTH),
369 TRXN_END,
370 };
371
372 BYTE copy_addr = subkey_byte[ subkey ] ;
373 BYTE copy_scratch[] = { _1W_COPY_SCRATCHPAD, copy_addr, BYTE_INVERSE(copy_addr), } ;
374 struct transaction_log twrite[] = {
375 TRXN_START,
376 TRXN_WRITE3(copy_scratch),
377 TRXN_WRITE(cp_array[block_IDENT], _DS1991_COPY_BLOCK_LENGTH),
378 TRXN_WRITE(password, _DS1991_PASSWORD_LENGTH),
379 TRXN_END,
380 };
381
382 RETURN_BAD_IF_BAD( BUS_transaction(twrite,pn) ) ;
383 return BUS_transaction(tcopy, pn) ;
384 }
385
OW_write_password(int subkey,BYTE * old_password,BYTE * new_password,const struct parsedname * pn)386 static GOOD_OR_BAD OW_write_password( int subkey, BYTE * old_password, BYTE * new_password, const struct parsedname * pn )
387 {
388 BYTE scratch_addr = 0x0C + _DS1991_PASSWORD_START ;
389 BYTE write_scratch[] = { _1W_WRITE_SCRATCHPAD, scratch_addr, BYTE_INVERSE(scratch_addr), } ;
390 BYTE copy_addr = subkey_byte[ subkey ] ;
391 BYTE copy_scratch[] = { _1W_COPY_SCRATCHPAD, copy_addr, BYTE_INVERSE(copy_addr), } ;
392 struct transaction_log twrite[] = {
393 TRXN_START,
394 TRXN_WRITE3(write_scratch),
395 TRXN_WRITE(new_password, _DS1991_PASSWORD_LENGTH),
396 TRXN_END,
397 };
398 struct transaction_log tcopy[] = {
399 TRXN_START,
400 TRXN_WRITE3(copy_scratch),
401 TRXN_WRITE(cp_array[block_PASSWORD], _DS1991_COPY_BLOCK_LENGTH),
402 TRXN_WRITE(old_password, _DS1991_PASSWORD_LENGTH),
403 TRXN_END,
404 };
405
406 RETURN_BAD_IF_BAD( BUS_transaction(twrite,pn) ) ;
407 return BUS_transaction(tcopy, pn) ;
408 }
409
ToPassword(char * text,BYTE * psw)410 static GOOD_OR_BAD ToPassword( char * text, BYTE * psw )
411 {
412 unsigned int text_length = _DS1991_PASSWORD_LENGTH * 2 ;
413 char convert_text[ text_length + 1 ] ;
414
415 memset( convert_text, '0', text_length ) ;
416 convert_text[text_length] = '\0' ;
417 if ( text == NULL ) {
418 return gbBAD ;
419 }
420
421 if ( strlen( text ) > text_length ) {
422 LEVEL_DEBUG("Password extension <%s> longer than %d bytes" , text, _DS1991_PASSWORD_LENGTH ) ;
423 return gbBAD ;
424 }
425
426 strcpy( & convert_text[ text_length - strlen(text) ] , text ) ;
427 string2bytes( convert_text, psw, _DS1991_PASSWORD_LENGTH ) ;
428 return gbGOOD ;
429 }
430
431
432