~ubuntu-branches/ubuntu/hoary/postfix/hoary-security

« back to all changes in this revision

Viewing changes to src/util/attr_scan0.c

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2004-10-06 11:50:33 UTC
  • Revision ID: james.westby@ubuntu.com-20041006115033-ooo6yfg6kmoteu04
Tags: upstream-2.1.3
ImportĀ upstreamĀ versionĀ 2.1.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*++
 
2
/* NAME
 
3
/*      attr_scan0 3
 
4
/* SUMMARY
 
5
/*      recover attributes from byte stream
 
6
/* SYNOPSIS
 
7
/*      #include <attr.h>
 
8
/*
 
9
/*      int     attr_scan0(fp, flags, type, name, ..., ATTR_TYPE_END)
 
10
/*      VSTREAM fp;
 
11
/*      int     flags;
 
12
/*      int     type;
 
13
/*      char    *name;
 
14
/*
 
15
/*      int     attr_vscan0(fp, flags, ap)
 
16
/*      VSTREAM fp;
 
17
/*      int     flags;
 
18
/*      va_list ap;
 
19
/* DESCRIPTION
 
20
/*      attr_scan0() takes zero or more (name, value) request attributes
 
21
/*      and recovers the attribute values from the byte stream that was
 
22
/*      possibly generated by attr_print0().
 
23
/*
 
24
/*      attr_vscan0() provides an alternative interface that is convenient
 
25
/*      for calling from within a variadic function.
 
26
/*
 
27
/*      The input stream is formatted as follows, where (item)* stands
 
28
/*      for zero or more instances of the specified item, and where
 
29
/*      (item1 | item2) stands for choice:
 
30
/*
 
31
/* .in +5
 
32
/*      attr-list :== simple-attr* null
 
33
/* .br
 
34
/*      simple-attr :== attr-name null attr-value null
 
35
/* .br
 
36
/*      attr-name :== any string not containing null
 
37
/* .br
 
38
/*      attr-value :== any string not containing null
 
39
/* .br
 
40
/*      null :== the ASCII null character
 
41
/* .in
 
42
/*
 
43
/*      All attribute names and attribute values are sent as null terminated
 
44
/*      strings. Each string must be no longer than 4*var_line_limit
 
45
/*      characters including the terminator.
 
46
/*      These formatting rules favor implementations in C.
 
47
/*
 
48
/*      Normally, attributes must be received in the sequence as specified with
 
49
/*      the attr_scan0() argument list.  The input stream may contain additional
 
50
/*      attributes at any point in the input stream, including additional
 
51
/*      instances of requested attributes.
 
52
/*
 
53
/*      Additional input attributes or input attribute instances are silently
 
54
/*      skipped over, unless the ATTR_FLAG_EXTRA processing flag is specified
 
55
/*      (see below). This allows for some flexibility in the evolution of
 
56
/*      protocols while still providing the option of being strict where
 
57
/*      this is desirable.
 
58
/*
 
59
/*      Arguments:
 
60
/* .IP fp
 
61
/*      Stream to recover the input attributes from.
 
62
/* .IP flags
 
63
/*      The bit-wise OR of zero or more of the following.
 
64
/* .RS
 
65
/* .IP ATTR_FLAG_MISSING
 
66
/*      Log a warning when the input attribute list terminates before all
 
67
/*      requested attributes are recovered. It is always an error when the
 
68
/*      input stream ends without the newline attribute list terminator.
 
69
/* .IP ATTR_FLAG_EXTRA
 
70
/*      Log a warning and stop attribute recovery when the input stream
 
71
/*      contains an attribute that was not requested. This includes the
 
72
/*      case of additional instances of a requested attribute.
 
73
/* .IP ATTR_FLAG_MORE
 
74
/*      After recovering the requested attributes, leave the input stream
 
75
/*      in a state that is usable for more attr_scan0() operations from the
 
76
/*      same input attribute list.
 
77
/*      By default, attr_scan0() skips forward past the input attribute list
 
78
/*      terminator.
 
79
/* .IP ATTR_FLAG_STRICT
 
80
/*      For convenience, this value combines both ATTR_FLAG_MISSING and
 
81
/*      ATTR_FLAG_EXTRA.
 
82
/* .IP ATTR_FLAG_NONE
 
83
/*      For convenience, this value requests none of the above.
 
84
/* .RE
 
85
/* .IP type
 
86
/*      The type argument determines the arguments that follow.
 
87
/* .RS
 
88
/* .IP "ATTR_TYPE_NUM (char *, int *)"
 
89
/*      This argument is followed by an attribute name and an integer pointer.
 
90
/* .IP "ATTR_TYPE_LONG (char *, long *)"
 
91
/*      This argument is followed by an attribute name and a long pointer.
 
92
/* .IP "ATTR_TYPE_STR (char *, VSTRING *)"
 
93
/*      This argument is followed by an attribute name and a VSTRING pointer.
 
94
/* .IP "ATTR_TYPE_HASH (HTABLE *)"
 
95
/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
 
96
/*      All further input attributes are processed as string attributes.
 
97
/*      No specific attribute sequence is enforced.
 
98
/*      All attributes up to the attribute list terminator are read,
 
99
/*      but only the first instance of each attribute is stored.
 
100
/*      There can be no more than 1024 attributes in a hash table.
 
101
/* .sp
 
102
/*      The attribute string values are stored in the hash table under
 
103
/*      keys equal to the attribute name (obtained from the input stream).
 
104
/*      Values from the input stream are added to the hash table. Existing
 
105
/*      hash table entries are not replaced.
 
106
/* .sp
 
107
/*      N.B. This construct must be followed by an ATTR_TYPE_END argument.
 
108
/* .IP ATTR_TYPE_END
 
109
/*      This argument terminates the requested attribute list.
 
110
/* .RE
 
111
/* BUGS
 
112
/*      ATTR_TYPE_HASH (ATTR_TYPE_NAMEVAL) accepts attributes with arbitrary
 
113
/*      names from possibly untrusted sources.
 
114
/*      This is unsafe, unless the resulting table is queried only with
 
115
/*      known to be good attribute names.
 
116
/* DIAGNOSTICS
 
117
/*      attr_scan0() and attr_vscan0() return -1 when malformed input is
 
118
/*      detected (string too long, incomplete line, missing end marker).
 
119
/*      Otherwise, the result value is the number of attributes that were
 
120
/*      successfully recovered from the input stream (a hash table counts
 
121
/*      as the number of entries stored into the table).
 
122
/*
 
123
/*      Panic: interface violation. All system call errors are fatal.
 
124
/* SEE ALSO
 
125
/*      attr_print0(3) send attributes over byte stream.
 
126
/* LICENSE
 
127
/* .ad
 
128
/* .fi
 
129
/*      The Secure Mailer license must be distributed with this software.
 
130
/* AUTHOR(S)
 
131
/*      Wietse Venema
 
132
/*      IBM T.J. Watson Research
 
133
/*      P.O. Box 704
 
134
/*      Yorktown Heights, NY 10598, USA
 
135
/*--*/
 
136
 
 
137
/* System library. */
 
138
 
 
139
#include <sys_defs.h>
 
140
#include <stdarg.h>
 
141
#include <string.h>
 
142
#include <stdio.h>
 
143
 
 
144
/* Utility library. */
 
145
 
 
146
#include <msg.h>
 
147
#include <mymalloc.h>
 
148
#include <vstream.h>
 
149
#include <vstring.h>
 
150
#include <vstring_vstream.h>
 
151
#include <htable.h>
 
152
#include <attr.h>
 
153
 
 
154
/* Application specific. */
 
155
 
 
156
#define STR(x)  vstring_str(x)
 
157
#define LEN(x)  VSTRING_LEN(x)
 
158
 
 
159
/* attr_scan0_string - pull a string from the input stream */
 
160
 
 
161
static int attr_scan0_string(VSTREAM *fp, VSTRING *plain_buf, const char *context)
 
162
{
 
163
    int     ch;
 
164
 
 
165
    if ((ch = vstring_get_null(plain_buf, fp)) == VSTREAM_EOF) {
 
166
        msg_warn("%s on %s while reading %s",
 
167
                 vstream_ftimeout(fp) ? "timeout" : "premature end-of-input",
 
168
                 VSTREAM_PATH(fp), context);
 
169
        return (-1);
 
170
    }
 
171
    if (ch != 0) {
 
172
        msg_warn("unexpected end-of-input from %s while reading %s",
 
173
                 VSTREAM_PATH(fp), context);
 
174
        return (-1);
 
175
    }
 
176
    if (msg_verbose)
 
177
        msg_info("%s: %s", context, *STR(plain_buf) ? STR(plain_buf) : "(end)");
 
178
    return (ch);
 
179
}
 
180
 
 
181
/* attr_scan0_number - pull a number from the input stream */
 
182
 
 
183
static int attr_scan0_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf,
 
184
                                     const char *context)
 
185
{
 
186
    char    junk = 0;
 
187
    int     ch;
 
188
 
 
189
    if ((ch = attr_scan0_string(fp, str_buf, context)) < 0)
 
190
        return (-1);
 
191
    if (sscanf(STR(str_buf), "%u%c", ptr, &junk) != 1 || junk != 0) {
 
192
        msg_warn("malformed numerical data from %s while reading %s: %.100s",
 
193
                 VSTREAM_PATH(fp), context, STR(str_buf));
 
194
        return (-1);
 
195
    }
 
196
    return (ch);
 
197
}
 
198
 
 
199
/* attr_scan0_long_number - pull a number from the input stream */
 
200
 
 
201
static int attr_scan0_long_number(VSTREAM *fp, unsigned long *ptr,
 
202
                                          VSTRING *str_buf,
 
203
                                          const char *context)
 
204
{
 
205
    char    junk = 0;
 
206
    int     ch;
 
207
 
 
208
    if ((ch = attr_scan0_string(fp, str_buf, context)) < 0)
 
209
        return (-1);
 
210
    if (sscanf(STR(str_buf), "%lu%c", ptr, &junk) != 1 || junk != 0) {
 
211
        msg_warn("malformed numerical data from %s while reading %s: %.100s",
 
212
                 VSTREAM_PATH(fp), context, STR(str_buf));
 
213
        return (-1);
 
214
    }
 
215
    return (ch);
 
216
}
 
217
 
 
218
/* attr_vscan0 - receive attribute list from stream */
 
219
 
 
220
int     attr_vscan0(VSTREAM *fp, int flags, va_list ap)
 
221
{
 
222
    const char *myname = "attr_scan0";
 
223
    static VSTRING *str_buf = 0;
 
224
    static VSTRING *name_buf = 0;
 
225
    int     wanted_type = -1;
 
226
    char   *wanted_name;
 
227
    unsigned int *number;
 
228
    unsigned long *long_number;
 
229
    VSTRING *string;
 
230
    HTABLE *hash_table;
 
231
    int     ch;
 
232
    int     conversions;
 
233
 
 
234
    /*
 
235
     * Sanity check.
 
236
     */
 
237
    if (flags & ~ATTR_FLAG_ALL)
 
238
        msg_panic("%s: bad flags: 0x%x", myname, flags);
 
239
 
 
240
    /*
 
241
     * Initialize.
 
242
     */
 
243
    if (str_buf == 0) {
 
244
        str_buf = vstring_alloc(10);
 
245
        name_buf = vstring_alloc(10);
 
246
    }
 
247
 
 
248
    /*
 
249
     * Iterate over all (type, name, value) triples.
 
250
     */
 
251
    for (conversions = 0; /* void */ ; conversions++) {
 
252
 
 
253
        /*
 
254
         * Determine the next attribute type and attribute name on the
 
255
         * caller's wish list.
 
256
         * 
 
257
         * If we're reading into a hash table, we already know that the
 
258
         * attribute value is string-valued, and we get the attribute name
 
259
         * from the input stream instead. This is secure only when the
 
260
         * resulting table is queried with known to be good attribute names.
 
261
         */
 
262
        if (wanted_type != ATTR_TYPE_HASH) {
 
263
            wanted_type = va_arg(ap, int);
 
264
            if (wanted_type == ATTR_TYPE_END) {
 
265
                if ((flags & ATTR_FLAG_MORE) != 0)
 
266
                    return (conversions);
 
267
                wanted_name = "(list terminator)";
 
268
            } else if (wanted_type == ATTR_TYPE_HASH) {
 
269
                wanted_name = "(any attribute name or list terminator)";
 
270
                hash_table = va_arg(ap, HTABLE *);
 
271
                if (va_arg(ap, int) !=ATTR_TYPE_END)
 
272
                    msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END",
 
273
                              myname);
 
274
            } else {
 
275
                wanted_name = va_arg(ap, char *);
 
276
            }
 
277
        }
 
278
 
 
279
        /*
 
280
         * Locate the next attribute of interest in the input stream.
 
281
         */
 
282
        for (;;) {
 
283
 
 
284
            /*
 
285
             * Get the name of the next attribute. Hitting EOF is always bad.
 
286
             * Hitting the end-of-input early is OK if the caller is prepared
 
287
             * to deal with missing inputs.
 
288
             */
 
289
            if (msg_verbose)
 
290
                msg_info("%s: wanted attribute: %s",
 
291
                         VSTREAM_PATH(fp), wanted_name);
 
292
            if ((ch = attr_scan0_string(fp, name_buf,
 
293
                                    "input attribute name")) == VSTREAM_EOF)
 
294
                return (-1);
 
295
            if (LEN(name_buf) == 0) {
 
296
                if (wanted_type == ATTR_TYPE_END
 
297
                    || wanted_type == ATTR_TYPE_HASH)
 
298
                    return (conversions);
 
299
                if ((flags & ATTR_FLAG_MISSING) != 0)
 
300
                    msg_warn("missing attribute %s in input from %s",
 
301
                             wanted_name, VSTREAM_PATH(fp));
 
302
                return (conversions);
 
303
            }
 
304
 
 
305
            /*
 
306
             * See if the caller asks for this attribute.
 
307
             */
 
308
            if (wanted_type == ATTR_TYPE_HASH
 
309
                || (wanted_type != ATTR_TYPE_END
 
310
                    && strcmp(wanted_name, STR(name_buf)) == 0))
 
311
                break;
 
312
            if ((flags & ATTR_FLAG_EXTRA) != 0) {
 
313
                msg_warn("unexpected attribute %s in input from %s",
 
314
                         STR(name_buf), VSTREAM_PATH(fp));
 
315
                return (conversions);
 
316
            }
 
317
 
 
318
            /*
 
319
             * Skip over this attribute. The caller does not ask for it.
 
320
             */
 
321
            (void) attr_scan0_string(fp, str_buf, "input attribute value");
 
322
        }
 
323
 
 
324
        /*
 
325
         * Do the requested conversion.
 
326
         */
 
327
        switch (wanted_type) {
 
328
        case ATTR_TYPE_NUM:
 
329
            number = va_arg(ap, unsigned int *);
 
330
            if ((ch = attr_scan0_number(fp, number, str_buf,
 
331
                                        "input attribute value")) < 0)
 
332
                return (-1);
 
333
            break;
 
334
        case ATTR_TYPE_LONG:
 
335
            long_number = va_arg(ap, unsigned long *);
 
336
            if ((ch = attr_scan0_long_number(fp, long_number, str_buf,
 
337
                                             "input attribute value")) < 0)
 
338
                return (-1);
 
339
            break;
 
340
        case ATTR_TYPE_STR:
 
341
            string = va_arg(ap, VSTRING *);
 
342
            if ((ch = attr_scan0_string(fp, string,
 
343
                                        "input attribute value")) < 0)
 
344
                return (-1);
 
345
            break;
 
346
        case ATTR_TYPE_HASH:
 
347
            if ((ch = attr_scan0_string(fp, str_buf,
 
348
                                        "input attribute value")) < 0)
 
349
                return (-1);
 
350
            if (htable_locate(hash_table, STR(name_buf)) != 0) {
 
351
                if ((flags & ATTR_FLAG_EXTRA) != 0) {
 
352
                    msg_warn("duplicate attribute %s in input from %s",
 
353
                             STR(name_buf), VSTREAM_PATH(fp));
 
354
                    return (conversions);
 
355
                }
 
356
            } else if (hash_table->used >= ATTR_HASH_LIMIT) {
 
357
                msg_warn("attribute count exceeds limit %d in input from %s",
 
358
                         ATTR_HASH_LIMIT, VSTREAM_PATH(fp));
 
359
                return (conversions);
 
360
            } else {
 
361
                htable_enter(hash_table, STR(name_buf),
 
362
                             mystrdup(STR(str_buf)));
 
363
            }
 
364
            break;
 
365
        default:
 
366
            msg_panic("%s: unknown type code: %d", myname, wanted_type);
 
367
        }
 
368
    }
 
369
}
 
370
 
 
371
/* attr_scan0 - read attribute list from stream */
 
372
 
 
373
int     attr_scan0(VSTREAM *fp, int flags,...)
 
374
{
 
375
    va_list ap;
 
376
    int     ret;
 
377
 
 
378
    va_start(ap, flags);
 
379
    ret = attr_vscan0(fp, flags, ap);
 
380
    va_end(ap);
 
381
    return (ret);
 
382
}
 
383
 
 
384
#ifdef TEST
 
385
 
 
386
 /*
 
387
  * Proof of concept test program.  Mirror image of the attr_scan0 test
 
388
  * program.
 
389
  */
 
390
#include <msg_vstream.h>
 
391
 
 
392
int     var_line_limit = 2048;
 
393
 
 
394
int     main(int unused_argc, char **used_argv)
 
395
{
 
396
    VSTRING *str_val = vstring_alloc(1);
 
397
    HTABLE *table = htable_create(1);
 
398
    HTABLE_INFO **ht_info_list;
 
399
    HTABLE_INFO **ht;
 
400
    int     int_val;
 
401
    long    long_val;
 
402
    int     ret;
 
403
 
 
404
    msg_verbose = 1;
 
405
    msg_vstream_init(used_argv[0], VSTREAM_ERR);
 
406
    if ((ret = attr_scan0(VSTREAM_IN,
 
407
                          ATTR_FLAG_STRICT,
 
408
                          ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
 
409
                          ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
 
410
                          ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
 
411
                          ATTR_TYPE_HASH, table,
 
412
                          ATTR_TYPE_END)) > 3) {
 
413
        vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
 
414
        vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
 
415
        vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
 
416
        ht_info_list = htable_list(table);
 
417
        for (ht = ht_info_list; *ht; ht++)
 
418
            vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
 
419
        myfree((char *) ht_info_list);
 
420
    } else {
 
421
        vstream_printf("return: %d\n", ret);
 
422
    }
 
423
    if ((ret = attr_scan0(VSTREAM_IN,
 
424
                          ATTR_FLAG_STRICT,
 
425
                          ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
 
426
                          ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
 
427
                          ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
 
428
                          ATTR_TYPE_END)) == 3) {
 
429
        vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
 
430
        vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
 
431
        vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
 
432
        ht_info_list = htable_list(table);
 
433
        for (ht = ht_info_list; *ht; ht++)
 
434
            vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
 
435
        myfree((char *) ht_info_list);
 
436
    } else {
 
437
        vstream_printf("return: %d\n", ret);
 
438
    }
 
439
    if (vstream_fflush(VSTREAM_OUT) != 0)
 
440
        msg_fatal("write error: %m");
 
441
 
 
442
    vstring_free(str_val);
 
443
    htable_free(table, myfree);
 
444
 
 
445
    return (0);
 
446
}
 
447
 
 
448
#endif