summaryrefslogtreecommitdiffstats
path: root/src/signframework/utils.c
blob: f90f631c5fa1c1c999de4c7ca1b63f85aa005ff0 (plain)
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
/* Copyright 2017 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <ctype.h>
#include <errno.h>
#include <time.h>

#include "utils.h"

/* messages are traced here

   All messages, even error messages, are traced only if verbose is set.  There messages are
   'techie' and should not be returned unless the user asks for them.
*/

extern FILE* messageFile;
extern int verbose;

#define COPY_BLOCK_SIZE 1024

/* File_Copy() copies the source file to the destination file.
 */

int File_Copy(const char *destinationFilename,
              const char *sourceFilename)
{
    int 	rc = 0;
    char	buffer[COPY_BLOCK_SIZE];
    size_t	bytesRead;
    size_t	bytesWritten;
    FILE	*destination = NULL;	/* freed @1 */
    FILE	*source = NULL;		/* freed @2 */

    /* open the destination for write */
    if (rc == 0) {
        rc = File_Open(&destination, destinationFilename, "w");	/* closed @1 */
    }
    /* open the source for read */
    if (rc == 0) {
        rc = File_Open(&source, sourceFilename, "r");		/* closed @2 */
    }
    /* copy the source to the destination */
    while (rc == 0) {
        bytesRead = fread(buffer, 1, COPY_BLOCK_SIZE, source);
        if (bytesRead == 0) {
            if (feof(source)) {
                break;
            }
            else {
                if (verbose) fprintf(messageFile,
                                     "File_Copy: Error reading from %s\n", sourceFilename);
                rc = ERROR_CODE;
            }
        }
        bytesWritten = fwrite(buffer, 1, bytesRead, destination);
        if (bytesWritten != bytesRead) {
            if (verbose) fprintf(messageFile,
                                 "File_Copy: Error writing to %s\n", destinationFilename);
        }
    }
    /* close the destination */
    if (destination != NULL) {
        fclose(destination);	/* @1 */
    }
    /* close the source */
    if (source != NULL) {
        fclose(source);		/* @2 */
    }
    return rc;
}

/* File_Open() opens the 'filename' for 'mode'
 */

int File_Open(FILE **file,
              const char *filename,
              const char* mode)
{
    int 	rc = 0;

    if (rc == 0) {
        *file = fopen(filename, mode);
        if (*file == NULL) {
            if (verbose) {
                // Check to make sure we aren't attempting to reroute the message file
                if (file != &messageFile) {
                    fprintf(messageFile, "File_Open: Error opening %s for %s, %s\n",
                                 filename, mode, strerror(errno));
                } else {
                    fprintf(stderr, "File_Open: Error opening %s for %s, %s\n",
                                 filename, mode, strerror(errno));
                }
            }
            rc = ERROR_CODE;
        }
    }
    return rc;
}

/* File_OpenMessageFile() opens the global messageFile.  messageFile is used to fprintf messages to
   the returned output email body.

   For each new email, the framework first opens for write.  Then the signer program can open it for
   append.  Finally, the framework can then open for append to add any final messages.

   Returns responseType
*/

int File_OpenMessageFile(const char *outputBodyFilename,
                         const char* mode)
{
    int 	rc = 0;

    /* Switch messageFile error and trace printing from stdout to the output body.  The design sends
       framework initialization to stdout so the person initiating the program can see errors.  Once
       it's running, everything else goes to the output body and (usually) back to the user.  */
    if (rc == 0) {
        rc = File_Open(&messageFile, outputBodyFilename, mode);
    }
    /* if the open succeeded */
    if (rc == 0) {
        /* turn off buffering as a debug aid, so the file gets updated while stepping with a
           debugger */
        setvbuf(messageFile , 0, _IONBF, 0);
    }
    /* if the open failed */
    else {
        messageFile = stdout;
        fprintf(messageFile,
                "File_OpenMessageFile: Error cannot open %s\n", outputBodyFilename);
	    /* Since the configuration is validated at startup, this should never fail.  The only
	       possibilty is that something happened to the platform while the framework was
	       running.  No email can be returned and messages go to stdout. */
        rc = RESPONSE_NO_EMAIL;
    }
    return rc;
}

/* File_CloseMessageFile() flushes and then closes the global messageFile and them sets it to
   stdout.

   If messageFile is already stdout (or NULL), the function does nothing.
*/

int File_CloseMessageFile(void)
{
    int 	rc = 0;

    if ((messageFile != NULL) &&	/* should never happen after start up */
        (messageFile != stdout)) {
        fflush(messageFile);
        fclose(messageFile);
        messageFile = stdout;	/* should never print after this, but the messages should go
                                   somewhere */
    }
    return rc;
}

/* File_Readline() returns the next non-comment, non-whitespace line from the file.

   It replaces the white space at the end of a line with a NUL terminator.
*/

int File_ReadLine(int *haveLine,	/* TRUE is line returned, otherwise FALSE */
                  char *line,		/* returned line */
                  size_t *lineLength,	/* returned actual length */
                  size_t lineSize,	/* max size of line buffer */
                  FILE *file)		/* opened file to read */
{
    int 	rc = 0;
    char 	*prc = NULL;		/* pointer return code */

    *haveLine = FALSE;
    do {
        /* read the line */
        if (rc == 0) {
            prc = fgets(line, lineSize, file);
        }
        /* skip comment lines */
        if ((rc == 0) && (prc != NULL)) {
            if (line[0] == '#') {
                continue;
            }
        }
        /* skip lines beginning with whitespace */
        if ((rc == 0) && (prc != NULL)) {
            if (isspace(line[0])) {
                continue;
            }
        }
        if ((rc == 0) && (prc != NULL)) {
            /* found a line with text */
            *haveLine = TRUE;
            /* check for line overflow */
            *lineLength = strlen(line);
            if (line[*lineLength -1] != '\n') {	/* last character before NUL should be newline */
                if (verbose) fprintf(messageFile,
                                     "File_ReadLine: Error, Line %s is longer that %u bytes\n",
                                     line, (unsigned int)lineSize);
                rc = ERROR_CODE;
            }
        }
        /* strip off white space at the end of the line */
        if ((rc == 0) && (prc != NULL)) {
            while (*lineLength > 0) {
                if (isspace(line[(*lineLength) - 1])) {
                    line[(*lineLength) - 1] = '\0';
                    (*lineLength)--;
                }
                else {
                    break;
                }
            }
        }
        break;
    }
    while ((rc == 0) && (prc != NULL));
    return rc;
}

/* File_ReadBinaryFile() reads 'filename'.  The results are put into 'data', which must be freed by
   the caller.  'length' indicates the number of bytes read. 'length_max' is the maximum allowed
   length.

   If 'length_max' is zero, the caller trusts the file length.
*/

int File_ReadBinaryFile(unsigned char **data,     /* must be freed by caller */
                        size_t *length,
                        size_t length_max,
                        const char *filename)
{
    int		rc = 0;
    long	lrc;
    size_t	src;
    int		irc;
    FILE	*file = NULL;

    *data = NULL;
    *length = 0;
    /* open the file */
    if (rc == 0) {
        rc = File_Open(&file, filename, "rb");				/* closed @1 */
    }
    /* determine the file length */
    if (rc == 0) {
        irc = fseek(file, 0L, SEEK_END);	/* seek to end of file */
        if (irc == -1L) {
            if (verbose) fprintf(messageFile,
                                 "File_ReadBinaryFile: Error seeking to end of %s\n", filename);
            rc = ERROR_CODE;
        }
    }
    if (rc == 0) {
        lrc = ftell(file);			/* get position in the stream */
        if (lrc == -1L) {
            if (verbose) fprintf(messageFile,
                                 "File_ReadBinaryFile: Error ftell'ing %s\n", filename);
            rc = ERROR_CODE;
        }
        else {
            *length = (size_t)lrc;		/* save the length */
        }
    }
    if (rc == 0) {
        irc = fseek(file, 0L, SEEK_SET);	/* seek back to the beginning of the file */
        if (irc == -1L) {
            if (verbose) fprintf(messageFile,
                                 "File_ReadBinaryFile: Error seeking to beginning of %s\n",
                                 filename);
            rc = ERROR_CODE;
        }
    }
    /* allocate a buffer for the actual data */
    if ((rc == 0) && *length != 0) {
        /* if length_max is zero, the caller trusts the file length */
        if (length_max == 0) {
            length_max = *length;
        }
        rc = Malloc_Safe(data, *length, length_max);
    }
    /* read the contents of the file into the data buffer */
    if ((rc == 0) && *length != 0) {
        src = fread(*data, 1, *length, file);
        if (src != *length) {
            if (verbose) fprintf(messageFile,
                                 "File_ReadBinaryFile: Error reading %s, %u bytes\n",
                                 filename, (unsigned int)*length);
            rc = ERROR_CODE;
        }
    }
    if (file != NULL) {
        irc = fclose(file);		/* @1 */
        if (irc != 0) {
            if (verbose) fprintf(messageFile,
                                 "File_ReadBinaryFile: Error closing %s\n",
                                 filename);
            rc = ERROR_CODE;
        }
    }
    if (rc != 0) {
        if (verbose) fprintf(messageFile, "File_ReadBinaryFile: Error reading %s\n", filename);
        free(*data);
        data = NULL;
    }
    return rc;
}

/* File_ReadTextFile() reads 'filename'.  The results are put into 'text', which must be freed by
   the caller.  'length' indicates the number of bytes read.

   A NUL terminator is added to 'text', but the bytes are not scanned for e.g., printable
   characters.
*/

int File_ReadTextFile(char **text,     /* must be freed by caller */
                      size_t *length,
                      size_t length_max,
                      const char *filename)
{
    int rc = 0;

    /* read the file as raw binary data */
    if (rc == 0) {
        rc = File_ReadBinaryFile((unsigned char **)text, length, length_max, filename);
    }
    /* realloc one more byte for the NULL terminator */
    if (rc == 0) {
        rc = Realloc_Safe((unsigned char **)text, (*length) + 1);
    }
    /* NUL terminate the string */
    if (rc == 0) {
        (*text)[*length] = '\0';
    }
    return rc;
}

/* File_GetSize() opens a file for read and returns its length
 */

int File_GetSize(size_t *fileLength,
                 const char *filename)
{
    int		rc = 0;
    FILE	*file = NULL;		/* freed @1 */
    int         irc;
    long        lrc;

    if (rc == 0) {
        if (filename == NULL) {
            if (verbose) fprintf(messageFile, "File_GetSize: Error, filename is null\n");
            rc = ERROR_CODE;
        }
    }
    if (rc == 0) {
        rc = File_Open(&file, filename, "rb");	/* freed @1 */
    }
    /* determine the file length */
    if (rc == 0) {
        irc = fseek(file, 0L, SEEK_END);        /* seek to end of file */
        if (irc == -1L) {
            if (verbose) fprintf(messageFile, "File_GetSize: Error fseek'ing %s, %s\n",
                                 filename, strerror(errno));
            rc = ERROR_CODE;
        }
    }
    if (rc == 0) {
        lrc = ftell(file);                      /* get position in the stream */
        if (lrc == -1L) {
            if (verbose) fprintf(messageFile, "File_GetSize: Error (fatal) ftell'ing %s, %s\n",
                                 filename, strerror(errno));
            rc = ERROR_CODE;
        }
        else {
            *fileLength = lrc;      		/* save the length */
        }
    }
    if (file != NULL) {
        fclose(file);	/* @1 */
    }
    return rc;
}

/* File_ValidateOpen() validates that a file can be opened for 'mode'.  It then closes the file.
 */

int File_ValidateOpen(const char *filename,
                      const char *mode)
{
    int		rc = 0;
    FILE	*file = NULL;		/* freed @1 */

    if (rc == 0) {
        if (filename == NULL) {
            if (verbose) fprintf(messageFile, "File_ValidateOpen: Error, filename is null\n");
            rc = ERROR_CODE;
        }
    }
    if (rc == 0) {
        rc = File_Open(&file, filename, mode);	/* freed @1 */
    }
    /* immediately close */
    if (file != NULL) {
        fclose(file);		/* @1 */
    }
    return rc;
}

/* File_WriteBinaryFile() writes 'length' bytes of data to filename
 */

int File_WriteBinaryFile(const unsigned char *data,
                         size_t length,
                         const char *filename)
{
    long	rc = 0;
    size_t	src;
    int		irc;
    FILE	*file = NULL;

    /* open the file */
    if (rc == 0) {
        rc = File_Open(&file, filename, "wb");	/* closed @1 */
    }
    /* write the contents of the data buffer into the file */
    if (rc == 0) {
        src = fwrite(data, 1, length, file);
        if (src != length) {
            if (verbose) fprintf(messageFile, "File_WriteBinaryFile: Error writing %s\n",
                                 filename);
            rc = ERROR_CODE;
        }
    }
    if (file != NULL) {
        irc = fclose(file);		/* @1 */
        if (irc != 0) {
            if (verbose) fprintf(messageFile, "File_WriteBinaryFile: Error closing %s\n",
                                 filename);
            rc = ERROR_CODE;
        }
    }
    return rc;
}

/* File_WriteBinaryFileVa() writes filename with a varargs list of length / buffer pairs.  A zero
   length terminates the loop
*/

int File_WriteBinaryFileVa(const char *filename, ...)
{
    long		rc = 0;
    FILE		*file = NULL;		/* closed @1 */
    va_list		ap;
    uint32_t		length;
    unsigned char	*buffer;
    size_t		src;
    int			irc;
    int			done = FALSE;

    /* open the file */
    if (rc == 0) {
        rc = File_Open(&file, filename, "wb");	/* closed @1 */
    }
    if (rc == 0) {
        va_start(ap, filename);
    }
    while ((rc == 0) && !done) {
        length = va_arg(ap, size_t);		/* first vararg is the length */
        if (length != 0) {			/* loop until a zero length argument terminates */
            buffer = va_arg(ap, unsigned char *);	/* second vararg is the array */
            src = fwrite(buffer , 1, length, file);	/* write the buffer */
            if (src != length) {
                if (verbose) fprintf(messageFile, "File_WriteBinaryFileVa: Error writing %s\n",
                                     filename);
                rc = ERROR_CODE;
            }
        }
        else {
            done = TRUE;
        }
    }
    if (file != NULL) {
        irc = fclose(file);		/* @1 */
        if (irc != 0) {
            if (verbose) fprintf(messageFile, "File_WriteBinaryFileVa: Error closing %s\n",
                                 filename);
            rc = ERROR_CODE;
        }
    }
    return rc;
}

/* File_GetNameValue() reads the next non-comment, non-whitespace line from the file.

   If a line is found, it allocates memory and returns the 'name' and 'value'.  name and value are
   separated by a '=' character.
*/

int File_GetNameValue(int 	*haveLine,
                      char 	**name,		/* freed by caller */
                      char	**value,	/* freed by caller */
                      char 	*lineBuffer,
                      size_t	lineBufferLength,
                      FILE	*file)
{
    int		rc = 0;
    size_t 	lineLength;
    char 	*token;

    if (rc == 0) {
        rc = File_ReadLine(haveLine, lineBuffer, &lineLength, lineBufferLength, file);
    }
    /* get first token */
    if ((rc == 0) && *haveLine) {
        token = strtok(lineBuffer, "=");	/* get first token */
        if (token == NULL) {		/* malformed line */
            if (verbose) fprintf(messageFile, "File_GetNameValue: Error, bad format, missing =\n");
            if (verbose) fprintf(messageFile, "File_GetNameValue: Line: %s\n", lineBuffer);
            rc = ERROR_CODE;
        }
    }
    if ((rc == 0) && *haveLine) {
        rc = Malloc_Strcpy(name, token);
    }
    if ((rc == 0) && *haveLine) {
        token = strtok(NULL,"" );	/* get next token, rest of string */
        if (token == NULL) {		/* malformed line */
            if (verbose) fprintf(messageFile, "File_GetNameValue: Error, bad format, missing =\n");
            if (verbose) fprintf(messageFile, "File_GetNameValue: Line: %s\n", lineBuffer);
            rc = ERROR_CODE;
        }
    }
    if ((rc == 0) && *haveLine) {
        rc = Malloc_Strcpy(value, token);
    }
    return rc;
}

/* File_MapNameToValue() scans lines from file of the form

   name=value

   if found, memory is malloc'ed for the value and the value is copied
   if not found, an error is returned
*/

int File_MapNameToValue(char 		**value,		/* freed by caller */
                        const char 	*name,			/* name to search for */
                        char 		*lineBuffer,		/* supplied buffer for lines */
                        size_t 		lineBufferLength,	/* size of the line buffer */
                        FILE		*file)			/* input file stream */
{
    int		rc = 0;
    int		irc = 0;
    int 	haveLine;	/* true if more lines in the file stream */
    size_t 	lineLength;	/* length of the current line */
    char 	*token;		/* tokenizing the line */

    do {
        if (rc == 0) {
            rc = File_ReadLine(&haveLine, lineBuffer, &lineLength, lineBufferLength, file);
        }
        /* out of lines and no match is error */
        if (rc == 0) {
            if (!haveLine) {
                if (verbose) fprintf(messageFile,
                                     "File_MapNameToValue: Error, missing value for %s\n", name);
                rc = ERROR_CODE;
            }
        }
        /* get subject, first token */
        if (rc == 0) {
            token = strtok(lineBuffer, "=");		/* get first token */
            if (token == NULL) {		/* malformed line */
                if (verbose) fprintf(messageFile,
                                     "File_MapNameToValue: Error, bad format, missing =\n");
                rc = ERROR_CODE;
            }
        }
        /* compare name */
        if (rc == 0) {
            irc = strcmp(name, token);
            if (irc == 0) {
                /* match, done */
                break;
            }
        }
    }
    while (rc == 0);

    if (rc == 0) {
        token = strtok(NULL,"" );	/* get next token, rest of string */
        if (token == NULL) {		/* malformed line */
            if (verbose) fprintf(messageFile,
                                 "File_MapNameToValue: Error, bad format, missing value for %s\n",
                                 name);
            rc = ERROR_CODE;
        }
    }
    /* copy token to project file name */
    if (rc == 0) {
        rc = Malloc_Strcpy(value, token);
    }
    return rc;
}

/* File_MapNameToBool() scans lines from file of the form

   name=value

   if found, the value is checked for 'true' or 'false' and the boolean is returned.
   if not found, or the value is not true or false, an error is returned
*/

int File_MapNameToBool(int		*booln,
                       const char 	*name,
                       char 		*lineBuffer,
                       size_t 		lineBufferLength,
                       FILE		*file)
{
    int		rc = 0;
    char 	*booleanString = NULL;		/* freed @1 */

    if (rc == 0) {
        rc = File_MapNameToValue(&booleanString, /* freed by caller */
                                 name,
                                 lineBuffer,
                                 lineBufferLength,
                                 file);
    }
    /* look for true or false, no other string */
    if (rc == 0) {
        if (strcmp(booleanString, "true") == 0) {
            *booln = TRUE;
        }
        else if (strcmp(booleanString, "false") == 0) {
            *booln = FALSE;
        }
        else {
            if (verbose) fprintf(messageFile,
                                 "File_MapNameToBool: Error mapping %s, value is %s\n",
                                 name, booleanString);
            rc = ERROR_CODE;
        }
    }
    free(booleanString);	/* @1 */
    return rc;
}

/* File_MapNameToUint() scans lines from file of the form

   name=value

   if found, the value is checked for an unsigned integer and the integer is returned.
   if not found, or the value is not an unsigned integer, an error is returned
*/

int File_MapNameToUint(unsigned int	*value,
                       const char 	*name,
                       char 		*lineBuffer,
                       size_t 		lineBufferLength,
                       FILE		*file)
{
    int		rc = 0;
    int 	irc;
    char 	*intString = NULL;	/* freed @1 */
    char 	dummy;			/* extra characters at the end of the line */

    if (rc == 0) {
        rc = File_MapNameToValue(&intString, /* freed by caller */
                                 name,
                                 lineBuffer,
                                 lineBufferLength,
                                 file);
    }
    /* look for unsigned int, no other string */
    if (rc == 0) {
        irc = sscanf(intString, "%u%c", value, &dummy);
        if (irc != 1) {
            /* when this function returns an error, set the value to 0 so that the later delete
               doesn't think there are items to free */
            *value = 0;
            if (verbose) fprintf(messageFile,
                                 "File_MapNameToUint: Error mapping %s, value is %s\n",
                                 name, intString);
            rc = ERROR_CODE;
        }
    }
    free(intString);	/* @1 */
    return rc;
}

/* File_GetNameValueArray() parses the rest of 'file' for name=value pairs.

   It allocates the 'names' and 'values' arrays and fills in the elements

   lineBuffer is a temporary, to hold lines.
*/

int File_GetNameValueArray(char ***names,	/* freed by caller */
                           char ***values,	/* freed by caller */
                           size_t *length,	/* final length of the array */
                           char *lineBuffer,
                           size_t lineBufferLength,
                           FILE *file)
{
    int		rc = 0;
    int 	haveLine = TRUE;
    char	*name = NULL;
    char	*value = NULL;
    char 	*tmp;				/* used by realloc */

    *length = 0;

    /* check that names and values are NULL at entry, sanity check for memory leak */
    if (rc == 0) {
        if (names == NULL ||
            values == NULL) {
            if (verbose) fprintf(messageFile,
                                 "File_GetNameValueArray: names %p or values %p are NULL\n",
                                 names, values);
            rc = 1;
        }
        else if ((*names != NULL) || (*values != NULL)) {
            if (verbose) fprintf(messageFile,
                                 "File_GetNameValueArray: names %p or values %p not NULL\n",
                                 *names, *values);
            rc = 1;
        }
    }
    while ((rc == 0) && haveLine) {
        name = NULL;
        value = NULL;
        /* get a name - value pair */
        if (rc == 0) {
            rc = File_GetNameValue(&haveLine,
                                   &name,	/* freed by caller */
                                   &value,	/* freed by caller */
                                   lineBuffer,
                                   lineBufferLength,
                                   file);
        }
        /* if found a pair */
        if ((rc == 0) && haveLine) {
            /* increase the array size */
            (*length)++;
            /* grow the names array */
            tmp = realloc(*names, *length * sizeof(char *));
            if (tmp != NULL) {
                *names = (char **)tmp;
            }
            else {
                if (verbose) fprintf(messageFile,
                                     "File_GetNameValueArray: "
                                     "Error allocating memory for %u names\n",
                                     (unsigned int)*length);
                rc = ERROR_CODE;
            }
        }
        if ((rc == 0) && haveLine) {
            /* grow the values array */
            tmp = realloc(*values, *length * sizeof(char *));
            if (tmp != NULL) {
                *values = (char **)tmp;
            }
            else {
                if (verbose) fprintf(messageFile,
                                     "File_GetNameValueArray: "
                                     "Error allocating memory for %u values\n",
                                     (unsigned int)*length);
                rc = ERROR_CODE;
            }
        }
        if ((rc == 0) && haveLine) {
            /* assign name and value to array */
            (*names)[(*length) - 1] = name;
            (*values)[(*length) - 1] = value;
        }
    }
    return rc;
}

/* File_GetValueArray() parses 'file' for values.

   The number of values is determined by the first line, which is of the form

   'name'=integer

   It allocates the 'values' array and fills in the elements.
*/

int File_GetValueArray(char 		***values,	/* freed by caller */
                       size_t 		*length,	/* items in the array */
                       const char 	*name,
                       char 		*lineBuffer,
                       size_t 		lineBufferLength,
                       FILE 		*file)
{
    int		rc = 0;
    size_t	i;
    int 	haveLine = TRUE;
    size_t 	lineLength;

    *length = 0;

    /* check that values is NULL at entry, sanity check for memory leak */
    if (rc == 0) {
        if (*values != NULL) {
            if (verbose) fprintf(messageFile,
                                 "File_GetValueArray: values %p not NULL\n",
                                 *values);
        }
    }
    /* get the count of values in the array */
    if (rc == 0) {
        rc = File_MapNameToUint((unsigned int *)length,
                                name,
                                lineBuffer,
                                lineBufferLength,
                                file);
    }
    /* allocate the array for the values */
    if (rc == 0) {
        rc = Malloc_Safe((unsigned char **)values,	/* freed by caller */
                         *length * sizeof(char *),
                         *length * sizeof(char *));	/* trust the configuration file */
    }
    /* immediately NULL the array so it can be freed */
    for (i = 0 ; (rc == 0) && (i < *length) ; i++) {
        (*values)[i] = NULL;
    }
    /* for each expected value in the array */
    for (i = 0 ; (rc == 0) && (i < *length) ; i++) {
        /* each line is a value */
        if (rc == 0) {
            rc = File_ReadLine(&haveLine, lineBuffer, &lineLength, lineBufferLength, file);
        }
        /* insufficient lines is an error */
        if (rc == 0) {
            if (!haveLine) {
                if (verbose) fprintf(messageFile,
                                     "File_GetValueArray: Error, not %u entries for %s\n",
                                     (unsigned int)*length, name);
                rc = ERROR_CODE;
            }
        }
        /* allocate memory for the value and copy into the array entry */
        if (rc == 0) {
            rc = Malloc_Strcpy(&((*values)[i]), lineBuffer);	/* freed by caller */
        }
    }
    return rc;
}

/* File_LogTime() adds the current date and time to 'logFile'
 */

void File_LogTime(FILE *logFile)
{
    time_t      log_time;
    log_time = time(NULL);
    fprintf(logFile, "\n%s", ctime(&log_time));
    return;
}

/* File_Printf() does an fprintf of the same parameters to both lFile (typically a log file) and
   mfile (typically the messageFile, the output body).

   lFile is prefixed by a tab character.

   If either is NULL, that file is skipped.
*/

void File_Printf(FILE *lFile,
                 FILE *mFile,
                 const char *format,
                 ...)
{
    va_list va;

    va_start(va, format);

    /* print to the log file if it's not NULL */
    if (lFile != NULL) {
        fprintf(lFile,"\t");
        va_start(va, format);
        vfprintf(lFile, format, va);
        va_end(va);
    }
    /* print to the messages file if it's not NULL */
    if (mFile != NULL) {
        va_start(va, format);
        vfprintf(mFile, format, va);
        va_end(va);
    }
    return;
}

/* Arguments_Init() sets all elements of the argv array to NULL.

   This permits the free() to be safe.
*/

void Arguments_Init(Arguments *arguments)
{
    size_t	i;

    arguments->argvBytes = 0;
    for (i = 0 ; i < MAX_ARGV_BODY ; i++) {
        arguments->argv[i] = NULL;
    }
    return;
}

/* Argvuments_Delete() frees all elements of argv and sets them back to NULL.

   Secret values are cleared before the memory is freed.  These are currently:

   -pwd
*/

void Arguments_Delete(Arguments *arguments)
{
    size_t	i;

    /* clear the plaintext password */
    Arguments_ClearSecret("-pwd", arguments);
    /* free allocated memory */
    for (i = 0 ; i < MAX_ARGV_BODY ; i++) {
        free(arguments->argv[i]);
        arguments->argv[i] = NULL;
    }
    return;
}

/* Arguments_ClearSecret() looks for 'flag' and zeros the value associated with the flag
 */

void Arguments_ClearSecret(const char 	*flag,
                           Arguments *arguments)
{
    size_t	i;
    size_t	length;
    int		irc;

    /* start at 1, argvBody[0] is the name of the program, not a flag */
    for (i = 1 ; i < (size_t)arguments->argc ; i++) {
        irc = strcmp(arguments->argv[i], flag);
        if (irc == 0) {		/* found a flag match */
            i++;		/* advance past the flag to the value */
            length = strlen(arguments->argv[i]);
            memset(arguments->argv[i], '\0', length);
        }
    }
    return;
}

/* Arguments_AddPairTo() adds the C strings 'flag' and 'value' pair to argv.  argc and argvBytes are
   updated accordingly.
*/

int Arguments_AddPairTo(Arguments *arguments,
                        const char 	*flag,
                        const char 	*value)
{
    int		rc = 0;

    /* check flag format */
    if (rc == 0) {
    }
    /* add flag */
    if (rc == 0) {
        rc = Arguments_AddTo(arguments,
                             flag, FALSE);
    }
    /* add value */
    if (rc == 0) {
        rc = Arguments_AddTo(arguments,
                             value, FALSE);
    }
    return rc;
}

/* Arguments_AddTo() adds the C string 'data' to argvBody.  argvBytes is updated accordingly.

   If zero is TRUE, the item is added at argv[0].  If FALSE, the item is added at the end and argc
   is updated accordingly.

   'zero' handles the speccial case of the program name.
*/

int Arguments_AddTo(Arguments 	*arguments,
                    const char 	*data,
                    int zero)
{
    int		rc = 0;
    size_t	i;
    size_t	length;

    /* check for NULL argument */
    if (rc == 0) {
        if (data == NULL) {
            if (verbose) fprintf(messageFile,
                                 "Arguments_AddTo: Error, adding NULL argument\n");
            rc = ERROR_CODE;
        }
    }
    /* check for array overflow */
    if (rc == 0) {
        length = strlen(data);
    }
    /* check data for illegal characters */
    for (i = 0 ; (rc == 0) && (i < length) ; i++) {
        if (!isprint(data[i])) {
            if (verbose) fprintf(messageFile,
                                 "Arguments_AddTo: Error, argument not printable at index %u\n",
                                 (unsigned int)i);
            rc = ERROR_CODE;
        }
    }
    /* check for array overflow.  This is a framwork compile time limitation.  The -1 reserves the
       last entry as a NULL, needed by exec() */
    if ((rc == 0) && !zero) {
        if (arguments->argc > (MAX_ARGV_BODY-1)) {
            if (verbose) fprintf(messageFile,
                                 "Arguments_AddTo: Error, overflows array of %u entries\n",
                                 MAX_ARGV_BODY);
            rc = ERROR_CODE;

        }
    }
    /* check for total bytes overflow.  This is a platform OS limitation */
    if (rc == 0) {
        if ((arguments->argvBytes + length) > ARG_MAX) {
            if (verbose) fprintf(messageFile,
                                 "Arguments_AddTo: Error, %s overflows argument list length\n",
                                 data);
            rc = ERROR_CODE;
        }
    }
    /* malloc and copy */
    if (rc == 0) {
        if (!zero) {
            rc = Malloc_Strcpy(&(arguments->argv[arguments->argc]), data);
        }
        else {
            rc = Malloc_Strcpy(&(arguments->argv[0]), data);
        }
    }
    if (rc == 0) {
        if (!zero) {
            arguments->argc++;		/* adjust the argument count */
        }
        arguments->argvBytes += length;	/* adjust the total number of bytes */
    }
    return rc;
}

/* Arguments_GetFrom() iterates through arguments->argv, searching for flag.

   If found, the value is returned.  The value is a pointer into arguments->argv.  It is not
   allocated and should not be freed.

   If not found, an error is returned.
*/

int Arguments_GetFrom(const char 	**value,
                      const char 	*flag,
                      Arguments		*arguments)
{
    int		rc = 0;
    int 	irc;
    int		i;
    int 	found = FALSE;

    /* start with [1], [0] is the program name, not a command line argument */
    for (i = 1 ; !found && (i < arguments->argc) ; i++) {
        irc = strcmp(flag, arguments->argv[i]);
        if ((irc == 0) && ((i+1) < arguments->argc)) {	/* if a flag match */
            *value = arguments->argv[i+1];		/* return the value */
            found = TRUE;
        }
    }
    if (!found) {
        if (verbose) fprintf(messageFile, "Arguments_GetFrom: %s not found\n", flag);
        rc = ERROR_CODE;
    }
    return rc;
}

/*
  character array handling
*/

/* Array_GetLine() acts on a character array.

   It returns the next non-comment, non-whitespace line from the array.

   It replaces the white space at the end of a line with NUL.

   It also returns a pointer to the next line.

   Returns error if the line is too long or contains non-printable characters.
*/

int Array_GetLine(int *haveLine,		/* TRUE if line returned, otherwise FALSE */
                  char *outLine,		/* returned line */
                  const char **nextLine, 	/* returned pointer to next line */
                  size_t lineSize,		/* max size of line buffer */
                  const char *inLines,		/* input character array */
                  FILE *logFile)		/* audit log file */
{
    int 	rc = 0;
    size_t	i = 0;		/* index into outLine */
    const char 	*ptr;

    *haveLine = FALSE;
    ptr = inLines;	/* starting point */

    /* skip comment lines or lines beginning with whitespace */
    if (rc == 0) {
        /* skip comment lines or lines beginning with whitespace */
        while ((*ptr == '#') ||
               (isspace(*ptr))) {

            /* if the line should be ignored, search for next newline, then increment past it */
            for ( ; *ptr != '\0' ; ptr++) {
                if (*ptr == '\n') {
                    ptr++;	/* point to next line and loop back to check for skip */
                    break;
                }
            }
        }
        /* found another line (or at the end of the buffer)  */
        /* scan to the end of the current line or the end of the buffer */
        while ((*ptr != '\0') && (*ptr != '\n')) {

            *haveLine = TRUE;

            /* check for overflow, leave space for NUL */
            if (i == (lineSize-1)) {
                outLine[lineSize-1] = '\0';	/* terminate the line for error message */
                File_Printf(logFile, messageFile,
                            "Error, Line is longer than %u bytes: %s\n",
                            lineSize, outLine);
                if (verbose) fprintf(messageFile,
                                     "Array_GetLine: Error, Line is longer than %u bytes: %s\n",
                                     (unsigned int)lineSize, outLine);
                rc = ERROR_CODE;
                break;
            }
            /* scan for non-printable characters */
            if (!isprint(*ptr)) {
                outLine[i] = '\0';		/* terminate the line for error message */
                File_Printf(logFile, messageFile,
                            "Error, Line %s has non-printable character at index %u\n",
                            outLine, i);
                if (verbose) fprintf(messageFile,
                                     "Array_GetLine: "
                                     "Error, Line %s has non-printable character at index %u\n",
                                     outLine, (unsigned int)i);
                rc = ERROR_CODE;
                break;
            }
            /* character valid, copy from input array to output line */
            outLine[i] = *ptr;
            ptr++;	/* next input character */
            i++;	/* next output character */
        }
    }
    /* set next line */
    if (rc == 0) {
        if (*ptr == '\0') {	/* if finished, just point to NUL terminator for input array */
            *nextLine = ptr;
        }
        else {
            *nextLine = ptr+1;	/* if more lines, point past newline */
        }
    }
    if ((rc == 0) && *haveLine) {
        outLine[i] = '\0';		/* terminate the line */
        i--;	/* search back from last character */
        /* strip off white space at the end of the line */
        for ( ; (i > 0) && isspace(outLine[i]) ; i--) {
            outLine[i] = '\0';
        }
        if (outLine[0] == '\0') {
            if (verbose) fprintf(messageFile,
                                 "Array_GetLine: Error: Line has only whitespace\n");
            rc = ERROR_CODE;	/* this should never occur, line with all whitespace should be
                                   ignored */
        }
    }
    return rc;
}

/* Malloc_Strcpy() malloc's an array for the 'in' string and then copies the string and the
   terminating NUL */

int Malloc_Strcpy(char 		**out,		/* freed by caller */
                  const char 	*in)
{
    int		rc = 0;

    /* malloc for the data */
    if (rc == 0) {
        rc = Malloc_Safe((unsigned char **)out,
                         strlen(in) + 1,
                         strlen(in) + 1);	/* trust configuration files */
    }
    /* copy the data */
    if (rc == 0) {
        strcpy(*out, in);
    }
    return rc;
}

/* Malloc_Safe() is a wrapper around malloc that detects memory leaks, uninitialized pointers, or an
   unreasonably large request */

int Malloc_Safe(unsigned char **ptr, size_t len, size_t max_length)
{
    int rc = 0;

    if (rc == 0) {
        if (len > max_length) {
            if (verbose) fprintf(messageFile,
                                 "Malloc_Safe: Error, length %u too large\n", (unsigned int)len);
            rc = ERROR_CODE;
        }
    }
    if (rc == 0) {
        if (*ptr != NULL) {
            if (verbose) fprintf(messageFile,
                                 "Malloc_Safe: Error, pointer is not NULL : %p\n", *ptr);
            rc = ERROR_CODE;
        }
    }
    if (rc == 0) {
        *ptr = malloc(len);
        if (*ptr == NULL) {
            if (verbose) fprintf(messageFile,
                                 "Malloc_Safe: Error, could not allocate %u bytes : %p\n", (unsigned int)len, *ptr);
            rc = ERROR_CODE;
        }
    }
    return rc;
}

/* Realloc_Safe() is a general purpose wrapper around realloc()

   The caller is responsible for validating that 'size' is reasonable.
*/

int Realloc_Safe(unsigned char **buffer,
                 size_t size)
{
    int 		rc = 0;
    unsigned char       *tmpptr = NULL;

    if (rc == 0) {
        tmpptr = realloc(*buffer, size);
        if (tmpptr == NULL) {
            if (verbose) fprintf(messageFile,
                                 "Realloc_Safe: Error reallocating %u bytes\n", (unsigned int)size);
            rc = ERROR_CODE;
        }
    }
    if (rc == 0) {
        *buffer = tmpptr;
    }
    return rc;
}

/*
  Format Conversion
*/

/* Format_ToHexascii() converts binary to hex ascii and appends a NUL terminator */

void Format_ToHexascii(char *string,
                       unsigned char *binary,
                       size_t length)
{
    size_t	i;

    for (i = 0 ; i < length ; i++, binary++, string += 2) {
        sprintf(string, "%02x", *binary);
    }
    return;
}

/* Format_FromHexAscii() converts 'string' in hex ascii to 'binary' of 'length'

   It assumes that the string has enough bytes to accommodate the length.
*/

int Format_FromHexascii(unsigned char *binary,
                        const char *string,
                        size_t length)
{
    int 	rc = 0;
    size_t	i;

    for (i = 0 ; (rc == 0) && (i < length) ; i++) {
        rc = Format_ByteFromHexascii(binary + i,
                                     string + (i * 2));

    }
    return rc;
}

/* Format_ByteFromHexAscii() converts two bytes of hex ascii to one byte of binary
 */

int Format_ByteFromHexascii(unsigned char *byte,
                            const char *string)
{
    int 	rc = 0;
    size_t	i;
    char	c;
    *byte 	= 0;

    for (i = 0 ; (rc == 0) && (i < 2) ; i++) {
        (*byte) <<= 4;		/* big endian, shift up the nibble */
        c = *(string + i);	/* extract the next character from the string */

        if ((c >= '0') && (c <= '9')) {
            *byte += c - '0';
        }
        else if ((c >= 'a') && (c <= 'f')) {
            *byte += c + 10 - 'a';
        }
        else if ((c >= 'A') && (c <= 'F')) {
            *byte += c + 10 - 'A';
        }
        else {
            if (verbose) fprintf(messageFile,
                                 "Format_ByteFromHexascii: "
                                 "Error: Line has non hex ascii character: %c\n", c);
            rc = ERROR_CODE;
        }
    }
    return rc;
}
OpenPOWER on IntegriCloud