Coverage Report

Created: 2025-12-31 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/MapServer/src/mapcrypto.c
Line
Count
Source
1
/******************************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  Encryption functions (see MS-RFC-18)
6
 * Author:   Daniel Morissette
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1996-2006 Regents of the University of Minnesota.
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a
12
 * copy of this software and associated documentation files (the "Software"),
13
 * to deal in the Software without restriction, including without limitation
14
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15
 * and/or sell copies of the Software, and to permit persons to whom the
16
 * Software is furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies of this Software or works derived from this Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
 * DEALINGS IN THE SOFTWARE.
28
 ****************************************************************************/
29
30
#include <assert.h>
31
#include <ctype.h>  /* isxdigit() */
32
#include <stdlib.h> /* rand() */
33
#include <time.h>   /* time() */
34
35
#include "mapserver.h"
36
37
#include "cpl_conv.h"
38
39
/**********************************************************************
40
 * encipher() and decipher() from the Tiny Encryption Algorithm (TEA)
41
 * website at:
42
 *   http://www.simonshepherd.supanet.com/tea.htm
43
 *
44
 * TEA was developed and placed in the public domain by David Wheeler
45
 * and Roger Needham at the Computer Laboratory of Cambridge University.
46
 *
47
 * The source below came with the following public domain notice:
48
 *
49
 *   "Please feel free to use any of this code in your applications.
50
 *    The TEA algorithm (including new-variant TEA) has been placed
51
 *    in the public domain, as have my assembly language implementations."
52
 *
53
 * ... and the following usage notes:
54
 *
55
 * All the routines have the general form
56
 *
57
 *  void encipher(const unsigned long *const v,unsigned long *const w,
58
 *                const unsigned long * const k);
59
 *
60
 *  void decipher(const unsigned long *const v,unsigned long *const w,
61
 *                const unsigned long * const k);
62
 *
63
 * TEA takes 64 bits of data in v[0] and v[1], and 128 bits of key in
64
 * k[0] - k[3]. The result is returned in w[0] and w[1]. Returning the
65
 * result separately makes implementation of cipher modes other than
66
 * Electronic Code Book a little bit easier.
67
 *
68
 * TEA can be operated in any of the modes of DES.
69
 *
70
 * n is the number of iterations. 32 is ample, 16 is sufficient, as few
71
 * as eight should be OK for most applications, especially ones where
72
 * the data age quickly (real-time video, for example). The algorithm
73
 * achieves good dispersion after six iterations. The iteration count
74
 * can be made variable if required.
75
 *
76
 * Note this algorithm is optimised for 32-bit CPUs with fast shift
77
 * capabilities. It can very easily be ported to assembly language
78
 * on most CPUs.
79
 *
80
 * delta is chosen to be the Golden ratio ((5/4)1/2 - 1/2 ~ 0.618034)
81
 * multiplied by 232. On entry to decipher(), sum is set to be delta * n.
82
 * Which way round you call the functions is arbitrary: DK(EK(P)) = EK(DK(P))
83
 * where EK and DK are encryption and decryption under key K respectively.
84
 *
85
 **********************************************************************/
86
87
static void encipher(const ms_uint32 *const v, ms_uint32 *const w,
88
0
                     const ms_uint32 *const k) {
89
0
  register ms_uint32 y = v[0], z = v[1], sum = 0, delta = 0x9E3779B9, n = 32;
90
91
0
  while (n-- > 0) {
92
0
    y += ((z << 4 ^ z >> 5) + z) ^ (sum + k[sum & 3]);
93
0
    sum += delta;
94
0
    z += ((y << 4 ^ y >> 5) + y) ^ (sum + k[sum >> 11 & 3]);
95
0
  }
96
97
0
  w[0] = y;
98
0
  w[1] = z;
99
0
}
100
101
static void decipher(const ms_uint32 *const v, ms_uint32 *const w,
102
0
                     const ms_uint32 *const k) {
103
0
  register ms_uint32 y = v[0], z = v[1], sum = 0xC6EF3720, delta = 0x9E3779B9,
104
0
                     n = 32;
105
106
  /* sum = delta<<5, in general sum = delta * n */
107
108
0
  while (n-- > 0) {
109
0
    z -= ((y << 4 ^ y >> 5) + y) ^ (sum + k[sum >> 11 & 3]);
110
0
    sum -= delta;
111
0
    y -= ((z << 4 ^ z >> 5) + z) ^ (sum + k[sum & 3]);
112
0
  }
113
114
0
  w[0] = y;
115
0
  w[1] = z;
116
0
}
117
118
/**********************************************************************
119
 *                          msHexEncode()
120
 *
121
 * Hex-encode numbytes from in[] and return the result in out[].
122
 *
123
 * out[] should be preallocated by the caller to be at least 2*numbytes+1
124
 * (+1 for the terminating '\0')
125
 **********************************************************************/
126
0
void msHexEncode(const unsigned char *in, char *out, int numbytes) {
127
0
  char *hex = "0123456789ABCDEF";
128
129
0
  while (numbytes-- > 0) {
130
0
    *out++ = hex[*in / 16];
131
0
    *out++ = hex[*in % 16];
132
0
    in++;
133
0
  }
134
0
  *out = '\0';
135
0
}
136
137
/**********************************************************************
138
 *                          msHexDecode()
139
 *
140
 * Hex-decode numchars from in[] and return the result in out[].
141
 *
142
 * If numchars > 0 then only up to this number of chars from in[] are
143
 * processed, otherwise the full in[] string up to the '\0' is processed.
144
 *
145
 * out[] should be preallocated by the caller to be large enough to hold
146
 * the resulting bytes.
147
 *
148
 * Returns the number of bytes written to out[] which may be different from
149
 * numchars/2 if an error or a '\0' is encountered.
150
 **********************************************************************/
151
0
int msHexDecode(const char *in, unsigned char *out, int numchars) {
152
0
  int numbytes_out = 0;
153
154
  /* Make sure numchars is even */
155
0
  numchars = (numchars / 2) * 2;
156
157
0
  if (numchars < 2)
158
0
    numchars = -1; /* Will result in this value being ignored in the loop*/
159
160
0
  while (*in != '\0' && *(in + 1) != '\0' && numchars != 0) {
161
0
    *out = 0x10 * (*in >= 'A' ? ((*in & 0xdf) - 'A') + 10 : (*in - '0'));
162
0
    in++;
163
0
    *out += (*in >= 'A' ? ((*in & 0xdf) - 'A') + 10 : (*in - '0'));
164
0
    in++;
165
166
0
    out++;
167
0
    numbytes_out++;
168
169
0
    numchars -= 2;
170
0
  }
171
172
0
  return numbytes_out;
173
0
}
174
175
/**********************************************************************
176
 *                       msGenerateEncryptionKey()
177
 *
178
 * Create a new encryption key.
179
 *
180
 * The output buffer should be at least MS_ENCRYPTION_KEY_SIZE bytes.
181
 **********************************************************************/
182
183
0
int msGenerateEncryptionKey(unsigned char *k) {
184
0
  int i;
185
186
  /* Use current time as seed for rand() */
187
0
  srand((unsigned int)time(NULL));
188
189
0
  for (i = 0; i < MS_ENCRYPTION_KEY_SIZE; i++) {
190
    /* coverity[dont_call] */
191
0
    k[i] = (unsigned char)rand();
192
0
  }
193
194
0
  return MS_SUCCESS;
195
0
}
196
197
/**********************************************************************
198
 *                       msReadEncryptionKeyFromFile()
199
 *
200
 * Read and decode hex-encoded encryption key from file and returns the
201
 * key in the 'unsigned char k[MS_ENCRYPTION_KEY_SIZE]' buffer that is
202
 * provided by the caller.
203
 *
204
 * Returns MS_SUCCESS/MS_FAILURE.
205
 **********************************************************************/
206
207
int msReadEncryptionKeyFromFile(const char *keyfile, unsigned char *k,
208
0
                                const char *pszRelToPath) {
209
0
  FILE *fp;
210
0
  char extended_path[MS_MAXPATHLEN];
211
0
  char szBuf[100];
212
0
  int numchars;
213
214
  /* Try to make the path relative */
215
0
  if (msBuildPath(extended_path, pszRelToPath, keyfile) == NULL)
216
0
    return MS_FAILURE;
217
218
0
  keyfile = extended_path;
219
220
0
  if ((fp = fopen(keyfile, "rt")) == NULL) {
221
0
    msSetError(MS_MISCERR, "Cannot open key file.",
222
0
               "msReadEncryptionKeyFromFile()");
223
0
    return MS_FAILURE;
224
0
  }
225
226
0
  numchars =
227
0
      fread(szBuf, sizeof(unsigned char), MS_ENCRYPTION_KEY_SIZE * 2, fp);
228
0
  fclose(fp);
229
0
  szBuf[MS_ENCRYPTION_KEY_SIZE * 2] = '\0';
230
231
0
  if (numchars != MS_ENCRYPTION_KEY_SIZE * 2) {
232
0
    msSetError(MS_MISCERR, "Invalid key file, got %d chars, expected %d.",
233
0
               "msReadEncryptionKeyFromFile()", numchars,
234
0
               MS_ENCRYPTION_KEY_SIZE * 2);
235
0
    return MS_FAILURE;
236
0
  }
237
238
0
  msHexDecode(szBuf, k, MS_ENCRYPTION_KEY_SIZE * 2);
239
240
0
  return MS_SUCCESS;
241
0
}
242
243
/**********************************************************************
244
 *                       msLoadEncryptionKey()
245
 *
246
 * Load and decode hex-encoded encryption key from file and returns the
247
 * key in the 'unsigned char k[MS_ENCRYPTION_KEY_SIZE]' buffer that is
248
 * provided by the caller.
249
 *
250
 * The first time that msLoadEncryptionKey() is called for a given mapObj
251
 * it will load the encryption key and cache it in mapObj->encryption_key.
252
 * If the key is already set in the mapObj then it does nothing and returns.
253
 *
254
 * The location of the encryption key can be specified in two ways,
255
 * either by setting the environment variable MS_ENCRYPTION_KEY or using
256
 * a CONFIG directive:
257
 *    CONFIG MS_ENCRYPTION_KEY "/path/to/mykey.txt"
258
 * Returns MS_SUCCESS/MS_FAILURE.
259
 **********************************************************************/
260
261
0
static int msLoadEncryptionKey(mapObj *map) {
262
0
  const char *keyfile;
263
264
0
  if (map == NULL) {
265
0
    msSetError(MS_MISCERR, "NULL MapObj.", "msLoadEncryptionKey()");
266
0
    return MS_FAILURE;
267
0
  }
268
269
0
  if (map->encryption_key_loaded)
270
0
    return MS_SUCCESS; /* Already loaded */
271
272
0
  keyfile = msGetConfigOption(map, "MS_ENCRYPTION_KEY");
273
0
  if (!keyfile)
274
0
    keyfile = CPLGetConfigOption("MS_ENCRYPTION_KEY", NULL);
275
276
0
  if (keyfile && msReadEncryptionKeyFromFile(keyfile, map->encryption_key,
277
0
                                             map->mappath) == MS_SUCCESS) {
278
0
    map->encryption_key_loaded = MS_TRUE;
279
0
  } else {
280
0
    msSetError(MS_MISCERR,
281
0
               "Failed reading encryption key. Make sure "
282
0
               "MS_ENCRYPTION_KEY is set and points to a valid key file.",
283
0
               "msLoadEncryptionKey()");
284
0
    return MS_FAILURE;
285
0
  }
286
287
0
  return MS_SUCCESS;
288
0
}
289
290
/**********************************************************************
291
 *                        msEncryptStringWithKey()
292
 *
293
 * Encrypts and hex-encodes the contents of string in[] and returns the
294
 * result in out[] which should have been pre-allocated by the caller
295
 * to be at least twice the size of in[] + 16+1 bytes (for padding + '\0').
296
 *
297
 **********************************************************************/
298
299
void msEncryptStringWithKey(const unsigned char *key, const char *in,
300
0
                            char *out) {
301
0
  ms_uint32 v[4], w[4];
302
0
  const ms_uint32 *k;
303
0
  int last_block = MS_FALSE;
304
305
  /* Casting the key this way is safe only as long as longs are 4 bytes
306
   * on this platform */
307
0
  assert(sizeof(ms_uint32) == 4);
308
0
  k = (const ms_uint32 *)key;
309
310
0
  while (!last_block) {
311
0
    int i, j;
312
    /* encipher() takes v[2] (64 bits) as input.
313
     * Copy bytes from in[] to the v[2] input array (pair of 4 bytes)
314
     * v[] is padded with zeros if string doesn't align with 8 bytes
315
     */
316
0
    v[0] = 0;
317
0
    v[1] = 0;
318
0
    for (i = 0; !last_block && i < 2; i++) {
319
0
      for (j = 0; j < 4; j++) {
320
0
        if (*in == '\0') {
321
0
          last_block = MS_TRUE;
322
0
          break;
323
0
        }
324
325
0
        v[i] |= *in << (j * 8);
326
0
        in++;
327
0
      }
328
0
    }
329
330
0
    if (*in == '\0')
331
0
      last_block = MS_TRUE;
332
333
    /* Do the actual encryption */
334
0
    encipher(v, w, k);
335
336
    /* Append hex-encoded bytes to output, 4 bytes at a time */
337
0
    msHexEncode((unsigned char *)w, out, 4);
338
0
    out += 8;
339
0
    msHexEncode((unsigned char *)(w + 1), out, 4);
340
0
    out += 8;
341
0
  }
342
343
  /* Make sure output is 0-terminated */
344
0
  *out = '\0';
345
0
}
346
347
/**********************************************************************
348
 *                        msDecryptStringWithKey()
349
 *
350
 * Hex-decodes and then decrypts the contents of string in[] and returns the
351
 * result in out[] which should have been pre-allocated by the caller
352
 * to be at least half the size of in[].
353
 *
354
 **********************************************************************/
355
356
void msDecryptStringWithKey(const unsigned char *key, const char *in,
357
0
                            char *out) {
358
0
  ms_uint32 v[4], w[4];
359
0
  const ms_uint32 *k;
360
0
  int last_block = MS_FALSE;
361
362
  /* Casting the key this way is safe only as long as longs are 4 bytes
363
   * on this platform */
364
0
  assert(sizeof(ms_uint32) == 4);
365
0
  k = (const ms_uint32 *)key;
366
367
0
  while (!last_block) {
368
0
    int i;
369
    /* decipher() takes v[2] (64 bits) as input.
370
     * Copy bytes from in[] to the v[2] input array (pair of 4 bytes)
371
     * v[] is padded with zeros if string doesn't align with 8 bytes
372
     */
373
0
    v[0] = 0;
374
0
    v[1] = 0;
375
376
0
    if (msHexDecode(in, (unsigned char *)v, 8) != 4)
377
0
      last_block = MS_TRUE;
378
0
    else {
379
0
      in += 8;
380
0
      if (msHexDecode(in, (unsigned char *)(v + 1), 8) != 4)
381
0
        last_block = MS_TRUE;
382
0
      else
383
0
        in += 8;
384
0
    }
385
386
    /* Do the actual decryption */
387
0
    decipher(v, w, k);
388
389
    /* Copy the results to out[] */
390
0
    for (i = 0; i < 2; i++) {
391
0
      *out++ = (w[i] & 0x000000ff);
392
0
      *out++ = (w[i] & 0x0000ff00) >> 8;
393
0
      *out++ = (w[i] & 0x00ff0000) >> 16;
394
0
      *out++ = (w[i] & 0xff000000) >> 24;
395
0
    }
396
397
0
    if (*in == '\0')
398
0
      last_block = MS_TRUE;
399
0
  }
400
401
  /* Make sure output is 0-terminated */
402
0
  *out = '\0';
403
0
}
404
405
/**********************************************************************
406
 *                        msDecryptStringTokens()
407
 *
408
 * Returns a newly allocated string (to be msFree'd by the caller) in
409
 * which all occurrences of encrypted strings delimited by {...} have
410
 * been decrypted.
411
 *
412
 **********************************************************************/
413
414
0
char *msDecryptStringTokens(mapObj *map, const char *in) {
415
0
  char *outbuf, *out;
416
417
0
  if (map == NULL) {
418
0
    msSetError(MS_MISCERR, "NULL MapObj.", "msLoadEncryptionKey()");
419
0
    return NULL;
420
0
  }
421
422
  /* Start with a copy of the string. Decryption can only result in
423
   * a string with the same or shorter length */
424
0
  if ((outbuf = (char *)malloc((strlen(in) + 1) * sizeof(char))) == NULL) {
425
0
    msSetError(MS_MEMERR, NULL, "msDecryptStringTokens()");
426
0
    return NULL;
427
0
  }
428
0
  out = outbuf;
429
430
0
  while (*in != '\0') {
431
0
    if (*in == '{') {
432
      /* Possibly beginning of a token, look for closing bracket
433
      ** and make sure all chars in between are valid hex encoding chars
434
      */
435
0
      const char *pszStart, *pszEnd;
436
0
      int valid_token = MS_FALSE;
437
438
0
      pszStart = in + 1;
439
0
      if ((pszEnd = strchr(pszStart, '}')) != NULL && pszEnd - pszStart > 1) {
440
0
        const char *pszTmp;
441
0
        valid_token = MS_TRUE;
442
0
        for (pszTmp = pszStart; pszTmp < pszEnd; pszTmp++) {
443
0
          if (!isxdigit(*pszTmp)) {
444
0
            valid_token = MS_FALSE;
445
0
            break;
446
0
          }
447
0
        }
448
0
      }
449
450
0
      if (valid_token) {
451
        /* Go ahead and decrypt the token */
452
0
        char *pszTmp;
453
454
        /* Make sure encryption key is loaded. We do this here instead
455
         * of at the beginning of the function to avoid loading the
456
         * key unless ready necessary. This is a very cheap call if
457
         * the key is already loaded
458
         */
459
0
        if (msLoadEncryptionKey(map) != MS_SUCCESS)
460
0
          return NULL;
461
462
0
        pszTmp = (char *)malloc((pszEnd - pszStart + 1) * sizeof(char));
463
0
        strlcpy(pszTmp, pszStart, (pszEnd - pszStart) + 1);
464
465
0
        msDecryptStringWithKey(map->encryption_key, pszTmp, out);
466
467
0
        out += strlen(out);
468
0
        in = pszEnd + 1;
469
0
        free(pszTmp);
470
0
      } else {
471
        /* Not a valid token, just copy the '{' and keep going */
472
0
        *out++ = *in++;
473
0
      }
474
0
    } else {
475
      /* Just copy any other chars */
476
0
      *out++ = *in++;
477
0
    }
478
0
  }
479
0
  *out = '\0';
480
481
0
  return outbuf;
482
0
}
483
484
#ifdef TEST_MAPCRYPTO
485
486
/* Test for mapcrypto.c functions. To run these tests, use the following
487
** Makefile directive:
488
489
test_mapcrypto: $(LIBMAP_STATIC) mapcrypto.c
490
  $(CC) $(CFLAGS) mapcrypto.c -DTEST_MAPCRYPTO $(EXE_LDFLAGS) -o test_mapcrypto
491
492
**
493
*/
494
int main(int argc, char *argv[]) {
495
  const unsigned char bytes_in[] = {0x12, 0x34, 0xff, 0x00, 0x44, 0x22};
496
  unsigned char bytes_out[8], encryption_key[MS_ENCRYPTION_KEY_SIZE * 2 + 1];
497
  char string_buf[256], string_buf2[256];
498
  int numbytes = 0;
499
500
  /*
501
  ** Test msHexEncode()
502
  */
503
  msHexEncode(bytes_in, string_buf, 6);
504
  printf("msHexEncode returned '%s'\n", string_buf);
505
506
  /*
507
  ** Test msHexDecode()
508
  */
509
  memset(bytes_out, 0, 8);
510
  numbytes = msHexDecode(string_buf, bytes_out, -1);
511
  printf(
512
      "msHexDecode(%s, -1) = %d, bytes_out = %x, %x, %x, %x, %x, %x, %x, %x\n",
513
      string_buf, numbytes, bytes_out[0], bytes_out[1], bytes_out[2],
514
      bytes_out[3], bytes_out[4], bytes_out[5], bytes_out[6], bytes_out[7]);
515
516
  memset(bytes_out, 0, 8);
517
  numbytes = msHexDecode(string_buf, bytes_out, 4);
518
  printf(
519
      "msHexDecode(%s, 4) = %d, bytes_out = %x, %x, %x, %x, %x, %x, %x, %x\n",
520
      string_buf, numbytes, bytes_out[0], bytes_out[1], bytes_out[2],
521
      bytes_out[3], bytes_out[4], bytes_out[5], bytes_out[6], bytes_out[7]);
522
523
  memset(bytes_out, 0, 8);
524
  numbytes = msHexDecode(string_buf, bytes_out, 20);
525
  printf(
526
      "msHexDecode(%s, 20) = %d, bytes_out = %x, %x, %x, %x, %x, %x, %x, %x\n",
527
      string_buf, numbytes, bytes_out[0], bytes_out[1], bytes_out[2],
528
      bytes_out[3], bytes_out[4], bytes_out[5], bytes_out[6], bytes_out[7]);
529
530
  /*
531
  ** Test loading encryption key
532
  */
533
  if (msReadEncryptionKeyFromFile("/tmp/test.key", encryption_key, NULL) !=
534
      MS_SUCCESS) {
535
    printf("msReadEncryptionKeyFromFile() = MS_FAILURE\n");
536
    printf("Aborting tests!\n");
537
    msWriteError(stderr);
538
    return -1;
539
  } else {
540
    msHexEncode(encryption_key, string_buf, MS_ENCRYPTION_KEY_SIZE);
541
    printf("msReadEncryptionKeyFromFile() returned '%s'\n", string_buf);
542
  }
543
544
  /*
545
  ** Test Encryption/Decryption
546
  */
547
548
  /* First with an 8 bytes input string (test boundaries) */
549
  msEncryptStringWithKey(encryption_key, "test1234", string_buf);
550
  printf("msEncryptStringWithKey('test1234') returned '%s'\n", string_buf);
551
552
  msDecryptStringWithKey(encryption_key, string_buf, string_buf2);
553
  printf("msDecryptStringWithKey('%s') returned '%s'\n", string_buf,
554
         string_buf2);
555
556
  /* Next with an 1 byte input string */
557
  msEncryptStringWithKey(encryption_key, "t", string_buf);
558
  printf("msEncryptStringWithKey('t') returned '%s'\n", string_buf);
559
560
  msDecryptStringWithKey(encryption_key, string_buf, string_buf2);
561
  printf("msDecryptStringWithKey('%s') returned '%s'\n", string_buf,
562
         string_buf2);
563
564
  /* Next with an 12 bytes input string */
565
  msEncryptStringWithKey(encryption_key, "test123456", string_buf);
566
  printf("msEncryptStringWithKey('test123456') returned '%s'\n", string_buf);
567
568
  msDecryptStringWithKey(encryption_key, string_buf, string_buf2);
569
  printf("msDecryptStringWithKey('%s') returned '%s'\n", string_buf,
570
         string_buf2);
571
572
  /*
573
  ** Test decryption with tokens
574
  */
575
  {
576
    char *pszBuf;
577
    mapObj *map;
578
    /* map = msNewMapObj(); */
579
    map = msLoadMap("/tmp/test.map", NULL);
580
581
    sprintf(string_buf2, "string with a {%s} encrypted token", string_buf);
582
583
    pszBuf = msDecryptStringTokens(map, string_buf2);
584
    if (pszBuf == NULL) {
585
      printf("msDecryptStringTokens() failed.\n");
586
      printf("Aborting tests!\n");
587
      msWriteError(stderr);
588
      return -1;
589
    } else {
590
      printf("msDecryptStringTokens('%s') returned '%s'\n", string_buf2,
591
             pszBuf);
592
    }
593
    msFree(pszBuf);
594
    msFreeMap(map);
595
  }
596
597
  return 0;
598
}
599
600
#endif /* TEST_MAPCRYPTO */