1183234Ssimon/* asn_mime.c */
2296465Sdelphij/*
3296465Sdelphij * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
4183234Ssimon * project.
5183234Ssimon */
6183234Ssimon/* ====================================================================
7183234Ssimon * Copyright (c) 1999-2008 The OpenSSL Project.  All rights reserved.
8183234Ssimon *
9183234Ssimon * Redistribution and use in source and binary forms, with or without
10183234Ssimon * modification, are permitted provided that the following conditions
11183234Ssimon * are met:
12183234Ssimon *
13183234Ssimon * 1. Redistributions of source code must retain the above copyright
14296465Sdelphij *    notice, this list of conditions and the following disclaimer.
15183234Ssimon *
16183234Ssimon * 2. Redistributions in binary form must reproduce the above copyright
17183234Ssimon *    notice, this list of conditions and the following disclaimer in
18183234Ssimon *    the documentation and/or other materials provided with the
19183234Ssimon *    distribution.
20183234Ssimon *
21183234Ssimon * 3. All advertising materials mentioning features or use of this
22183234Ssimon *    software must display the following acknowledgment:
23183234Ssimon *    "This product includes software developed by the OpenSSL Project
24183234Ssimon *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
25183234Ssimon *
26183234Ssimon * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
27183234Ssimon *    endorse or promote products derived from this software without
28183234Ssimon *    prior written permission. For written permission, please contact
29183234Ssimon *    licensing@OpenSSL.org.
30183234Ssimon *
31183234Ssimon * 5. Products derived from this software may not be called "OpenSSL"
32183234Ssimon *    nor may "OpenSSL" appear in their names without prior written
33183234Ssimon *    permission of the OpenSSL Project.
34183234Ssimon *
35183234Ssimon * 6. Redistributions of any form whatsoever must retain the following
36183234Ssimon *    acknowledgment:
37183234Ssimon *    "This product includes software developed by the OpenSSL Project
38183234Ssimon *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
39183234Ssimon *
40183234Ssimon * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
41183234Ssimon * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42183234Ssimon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43183234Ssimon * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
44183234Ssimon * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45183234Ssimon * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46183234Ssimon * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47183234Ssimon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48183234Ssimon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
49183234Ssimon * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50183234Ssimon * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
51183234Ssimon * OF THE POSSIBILITY OF SUCH DAMAGE.
52183234Ssimon * ====================================================================
53183234Ssimon *
54183234Ssimon */
55183234Ssimon
56183234Ssimon#include <stdio.h>
57183234Ssimon#include <ctype.h>
58183234Ssimon#include "cryptlib.h"
59183234Ssimon#include <openssl/rand.h>
60183234Ssimon#include <openssl/x509.h>
61183234Ssimon#include <openssl/asn1.h>
62183234Ssimon#include <openssl/asn1t.h>
63183234Ssimon
64296465Sdelphij/*
65296465Sdelphij * Generalised MIME like utilities for streaming ASN1. Although many have a
66296465Sdelphij * PKCS7/CMS like flavour others are more general purpose.
67183234Ssimon */
68183234Ssimon
69296465Sdelphij/*
70296465Sdelphij * MIME format structures Note that all are translated to lower case apart
71296465Sdelphij * from parameter values. Quotes are stripped off
72183234Ssimon */
73183234Ssimon
74183234Ssimontypedef struct {
75296465Sdelphij    char *param_name;           /* Param name e.g. "micalg" */
76296465Sdelphij    char *param_value;          /* Param value e.g. "sha1" */
77183234Ssimon} MIME_PARAM;
78183234Ssimon
79183234SsimonDECLARE_STACK_OF(MIME_PARAM)
80183234SsimonIMPLEMENT_STACK_OF(MIME_PARAM)
81183234Ssimon
82183234Ssimontypedef struct {
83296465Sdelphij    char *name;                 /* Name of line e.g. "content-type" */
84296465Sdelphij    char *value;                /* Value of line e.g. "text/plain" */
85296465Sdelphij    STACK_OF(MIME_PARAM) *params; /* Zero or more parameters */
86183234Ssimon} MIME_HEADER;
87183234Ssimon
88183234SsimonDECLARE_STACK_OF(MIME_HEADER)
89183234SsimonIMPLEMENT_STACK_OF(MIME_HEADER)
90183234Ssimon
91296465Sdelphijstatic char *strip_ends(char *name);
92296465Sdelphijstatic char *strip_start(char *name);
93296465Sdelphijstatic char *strip_end(char *name);
94183234Ssimonstatic MIME_HEADER *mime_hdr_new(char *name, char *value);
95183234Ssimonstatic int mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value);
96183234Ssimonstatic STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio);
97296465Sdelphijstatic int mime_hdr_cmp(const MIME_HEADER *const *a,
98296465Sdelphij                        const MIME_HEADER *const *b);
99296465Sdelphijstatic int mime_param_cmp(const MIME_PARAM *const *a,
100296465Sdelphij                          const MIME_PARAM *const *b);
101183234Ssimonstatic void mime_param_free(MIME_PARAM *param);
102183234Ssimonstatic int mime_bound_check(char *line, int linelen, char *bound, int blen);
103183234Ssimonstatic int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret);
104183234Ssimonstatic int strip_eol(char *linebuf, int *plen);
105183234Ssimonstatic MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name);
106183234Ssimonstatic MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name);
107183234Ssimonstatic void mime_hdr_free(MIME_HEADER *hdr);
108183234Ssimon
109183234Ssimon#define MAX_SMLEN 1024
110296465Sdelphij#define mime_debug(x)           /* x */
111183234Ssimon
112183234Ssimon/* Base 64 read and write of ASN1 structure */
113183234Ssimon
114183234Ssimonstatic int B64_write_ASN1(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
115296465Sdelphij                          const ASN1_ITEM *it)
116296465Sdelphij{
117296465Sdelphij    BIO *b64;
118296465Sdelphij    int r;
119296465Sdelphij    b64 = BIO_new(BIO_f_base64());
120296465Sdelphij    if (!b64) {
121296465Sdelphij        ASN1err(ASN1_F_B64_WRITE_ASN1, ERR_R_MALLOC_FAILURE);
122296465Sdelphij        return 0;
123296465Sdelphij    }
124296465Sdelphij    /*
125296465Sdelphij     * prepend the b64 BIO so all data is base64 encoded.
126296465Sdelphij     */
127296465Sdelphij    out = BIO_push(b64, out);
128296465Sdelphij    r = ASN1_item_i2d_bio(it, out, val);
129296465Sdelphij    (void)BIO_flush(out);
130296465Sdelphij    BIO_pop(out);
131296465Sdelphij    BIO_free(b64);
132296465Sdelphij    return r;
133296465Sdelphij}
134183234Ssimon
135183234Ssimonstatic ASN1_VALUE *b64_read_asn1(BIO *bio, const ASN1_ITEM *it)
136183234Ssimon{
137296465Sdelphij    BIO *b64;
138296465Sdelphij    ASN1_VALUE *val;
139296465Sdelphij    if (!(b64 = BIO_new(BIO_f_base64()))) {
140296465Sdelphij        ASN1err(ASN1_F_B64_READ_ASN1, ERR_R_MALLOC_FAILURE);
141296465Sdelphij        return 0;
142296465Sdelphij    }
143296465Sdelphij    bio = BIO_push(b64, bio);
144296465Sdelphij    val = ASN1_item_d2i_bio(it, bio, NULL);
145296465Sdelphij    if (!val)
146296465Sdelphij        ASN1err(ASN1_F_B64_READ_ASN1, ASN1_R_DECODE_ERROR);
147296465Sdelphij    (void)BIO_flush(bio);
148296465Sdelphij    bio = BIO_pop(bio);
149296465Sdelphij    BIO_free(b64);
150296465Sdelphij    return val;
151183234Ssimon}
152183234Ssimon
153183234Ssimon/* Generate the MIME "micalg" parameter from RFC3851, RFC4490 */
154183234Ssimon
155183234Ssimonstatic int asn1_write_micalg(BIO *out, STACK_OF(X509_ALGOR) *mdalgs)
156296465Sdelphij{
157296465Sdelphij    int i, have_unknown = 0, write_comma, md_nid;
158296465Sdelphij    have_unknown = 0;
159296465Sdelphij    write_comma = 0;
160296465Sdelphij    for (i = 0; i < sk_X509_ALGOR_num(mdalgs); i++) {
161296465Sdelphij        if (write_comma)
162296465Sdelphij            BIO_write(out, ",", 1);
163296465Sdelphij        write_comma = 1;
164296465Sdelphij        md_nid = OBJ_obj2nid(sk_X509_ALGOR_value(mdalgs, i)->algorithm);
165296465Sdelphij        switch (md_nid) {
166296465Sdelphij        case NID_sha1:
167296465Sdelphij            BIO_puts(out, "sha1");
168296465Sdelphij            break;
169183234Ssimon
170296465Sdelphij        case NID_md5:
171296465Sdelphij            BIO_puts(out, "md5");
172296465Sdelphij            break;
173183234Ssimon
174296465Sdelphij        case NID_sha256:
175296465Sdelphij            BIO_puts(out, "sha-256");
176296465Sdelphij            break;
177183234Ssimon
178296465Sdelphij        case NID_sha384:
179296465Sdelphij            BIO_puts(out, "sha-384");
180296465Sdelphij            break;
181183234Ssimon
182296465Sdelphij        case NID_sha512:
183296465Sdelphij            BIO_puts(out, "sha-512");
184296465Sdelphij            break;
185183234Ssimon
186296465Sdelphij        default:
187296465Sdelphij            if (have_unknown)
188296465Sdelphij                write_comma = 0;
189296465Sdelphij            else {
190296465Sdelphij                BIO_puts(out, "unknown");
191296465Sdelphij                have_unknown = 1;
192296465Sdelphij            }
193296465Sdelphij            break;
194183234Ssimon
195296465Sdelphij        }
196296465Sdelphij    }
197183234Ssimon
198296465Sdelphij    return 1;
199183234Ssimon
200296465Sdelphij}
201183234Ssimon
202183234Ssimon/* SMIME sender */
203183234Ssimon
204183234Ssimonint int_smime_write_ASN1(BIO *bio, ASN1_VALUE *val, BIO *data, int flags,
205296465Sdelphij                         int ctype_nid, int econt_nid,
206296465Sdelphij                         STACK_OF(X509_ALGOR) *mdalgs,
207296465Sdelphij                         asn1_output_data_fn * data_fn, const ASN1_ITEM *it)
208183234Ssimon{
209296465Sdelphij    char bound[33], c;
210296465Sdelphij    int i;
211296465Sdelphij    const char *mime_prefix, *mime_eol, *cname = "smime.p7m";
212296465Sdelphij    const char *msg_type = NULL;
213296465Sdelphij    if (flags & SMIME_OLDMIME)
214296465Sdelphij        mime_prefix = "application/x-pkcs7-";
215296465Sdelphij    else
216296465Sdelphij        mime_prefix = "application/pkcs7-";
217183234Ssimon
218296465Sdelphij    if (flags & SMIME_CRLFEOL)
219296465Sdelphij        mime_eol = "\r\n";
220296465Sdelphij    else
221296465Sdelphij        mime_eol = "\n";
222296465Sdelphij    if ((flags & SMIME_DETACHED) && data) {
223296465Sdelphij        /* We want multipart/signed */
224296465Sdelphij        /* Generate a random boundary */
225296465Sdelphij        RAND_pseudo_bytes((unsigned char *)bound, 32);
226296465Sdelphij        for (i = 0; i < 32; i++) {
227296465Sdelphij            c = bound[i] & 0xf;
228296465Sdelphij            if (c < 10)
229296465Sdelphij                c += '0';
230296465Sdelphij            else
231296465Sdelphij                c += 'A' - 10;
232296465Sdelphij            bound[i] = c;
233296465Sdelphij        }
234296465Sdelphij        bound[32] = 0;
235296465Sdelphij        BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol);
236296465Sdelphij        BIO_printf(bio, "Content-Type: multipart/signed;");
237296465Sdelphij        BIO_printf(bio, " protocol=\"%ssignature\";", mime_prefix);
238296465Sdelphij        BIO_puts(bio, " micalg=\"");
239296465Sdelphij        asn1_write_micalg(bio, mdalgs);
240296465Sdelphij        BIO_printf(bio, "\"; boundary=\"----%s\"%s%s",
241296465Sdelphij                   bound, mime_eol, mime_eol);
242296465Sdelphij        BIO_printf(bio, "This is an S/MIME signed message%s%s",
243296465Sdelphij                   mime_eol, mime_eol);
244296465Sdelphij        /* Now write out the first part */
245296465Sdelphij        BIO_printf(bio, "------%s%s", bound, mime_eol);
246296465Sdelphij        if (!data_fn(bio, data, val, flags, it))
247296465Sdelphij            return 0;
248296465Sdelphij        BIO_printf(bio, "%s------%s%s", mime_eol, bound, mime_eol);
249183234Ssimon
250296465Sdelphij        /* Headers for signature */
251183234Ssimon
252296465Sdelphij        BIO_printf(bio, "Content-Type: %ssignature;", mime_prefix);
253296465Sdelphij        BIO_printf(bio, " name=\"smime.p7s\"%s", mime_eol);
254296465Sdelphij        BIO_printf(bio, "Content-Transfer-Encoding: base64%s", mime_eol);
255296465Sdelphij        BIO_printf(bio, "Content-Disposition: attachment;");
256296465Sdelphij        BIO_printf(bio, " filename=\"smime.p7s\"%s%s", mime_eol, mime_eol);
257296465Sdelphij        B64_write_ASN1(bio, val, NULL, 0, it);
258296465Sdelphij        BIO_printf(bio, "%s------%s--%s%s", mime_eol, bound,
259296465Sdelphij                   mime_eol, mime_eol);
260296465Sdelphij        return 1;
261296465Sdelphij    }
262183234Ssimon
263296465Sdelphij    /* Determine smime-type header */
264183234Ssimon
265296465Sdelphij    if (ctype_nid == NID_pkcs7_enveloped)
266296465Sdelphij        msg_type = "enveloped-data";
267296465Sdelphij    else if (ctype_nid == NID_pkcs7_signed) {
268296465Sdelphij        if (econt_nid == NID_id_smime_ct_receipt)
269296465Sdelphij            msg_type = "signed-receipt";
270296465Sdelphij        else if (sk_X509_ALGOR_num(mdalgs) >= 0)
271296465Sdelphij            msg_type = "signed-data";
272296465Sdelphij        else
273296465Sdelphij            msg_type = "certs-only";
274296465Sdelphij    } else if (ctype_nid == NID_id_smime_ct_compressedData) {
275296465Sdelphij        msg_type = "compressed-data";
276296465Sdelphij        cname = "smime.p7z";
277296465Sdelphij    }
278296465Sdelphij    /* MIME headers */
279296465Sdelphij    BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol);
280296465Sdelphij    BIO_printf(bio, "Content-Disposition: attachment;");
281296465Sdelphij    BIO_printf(bio, " filename=\"%s\"%s", cname, mime_eol);
282296465Sdelphij    BIO_printf(bio, "Content-Type: %smime;", mime_prefix);
283296465Sdelphij    if (msg_type)
284296465Sdelphij        BIO_printf(bio, " smime-type=%s;", msg_type);
285296465Sdelphij    BIO_printf(bio, " name=\"%s\"%s", cname, mime_eol);
286296465Sdelphij    BIO_printf(bio, "Content-Transfer-Encoding: base64%s%s",
287296465Sdelphij               mime_eol, mime_eol);
288296465Sdelphij    if (!B64_write_ASN1(bio, val, data, flags, it))
289296465Sdelphij        return 0;
290296465Sdelphij    BIO_printf(bio, "%s", mime_eol);
291296465Sdelphij    return 1;
292183234Ssimon}
293183234Ssimon
294183234Ssimon#if 0
295183234Ssimon
296183234Ssimon/* Handle output of ASN1 data */
297183234Ssimon
298183234Ssimonstatic int asn1_output_data(BIO *out, BIO *data, ASN1_VALUE *val, int flags,
299296465Sdelphij                            const ASN1_ITEM *it)
300296465Sdelphij{
301296465Sdelphij    BIO *tmpbio;
302296465Sdelphij    const ASN1_AUX *aux = it->funcs;
303296465Sdelphij    ASN1_STREAM_ARG sarg;
304183234Ssimon
305296465Sdelphij    if (!(flags & SMIME_DETACHED)) {
306296465Sdelphij        SMIME_crlf_copy(data, out, flags);
307296465Sdelphij        return 1;
308296465Sdelphij    }
309183234Ssimon
310296465Sdelphij    if (!aux || !aux->asn1_cb) {
311296465Sdelphij        ASN1err(ASN1_F_ASN1_OUTPUT_DATA, ASN1_R_STREAMING_NOT_SUPPORTED);
312296465Sdelphij        return 0;
313296465Sdelphij    }
314183234Ssimon
315296465Sdelphij    sarg.out = out;
316296465Sdelphij    sarg.ndef_bio = NULL;
317296465Sdelphij    sarg.boundary = NULL;
318183234Ssimon
319296465Sdelphij    /* Let ASN1 code prepend any needed BIOs */
320183234Ssimon
321296465Sdelphij    if (aux->asn1_cb(ASN1_OP_DETACHED_PRE, &val, it, &sarg) <= 0)
322296465Sdelphij        return 0;
323183234Ssimon
324296465Sdelphij    /* Copy data across, passing through filter BIOs for processing */
325296465Sdelphij    SMIME_crlf_copy(data, sarg.ndef_bio, flags);
326183234Ssimon
327296465Sdelphij    /* Finalize structure */
328296465Sdelphij    if (aux->asn1_cb(ASN1_OP_DETACHED_POST, &val, it, &sarg) <= 0)
329296465Sdelphij        return 0;
330183234Ssimon
331296465Sdelphij    /* Now remove any digests prepended to the BIO */
332183234Ssimon
333296465Sdelphij    while (sarg.ndef_bio != out) {
334296465Sdelphij        tmpbio = BIO_pop(sarg.ndef_bio);
335296465Sdelphij        BIO_free(sarg.ndef_bio);
336296465Sdelphij        sarg.ndef_bio = tmpbio;
337296465Sdelphij    }
338183234Ssimon
339296465Sdelphij    return 1;
340183234Ssimon
341296465Sdelphij}
342183234Ssimon
343183234Ssimon#endif
344183234Ssimon
345296465Sdelphij/*
346296465Sdelphij * SMIME reader: handle multipart/signed and opaque signing. in multipart
347296465Sdelphij * case the content is placed in a memory BIO pointed to by "bcont". In
348296465Sdelphij * opaque this is set to NULL
349183234Ssimon */
350183234Ssimon
351183234SsimonASN1_VALUE *SMIME_read_ASN1(BIO *bio, BIO **bcont, const ASN1_ITEM *it)
352183234Ssimon{
353296465Sdelphij    BIO *asnin;
354296465Sdelphij    STACK_OF(MIME_HEADER) *headers = NULL;
355296465Sdelphij    STACK_OF(BIO) *parts = NULL;
356296465Sdelphij    MIME_HEADER *hdr;
357296465Sdelphij    MIME_PARAM *prm;
358296465Sdelphij    ASN1_VALUE *val;
359296465Sdelphij    int ret;
360183234Ssimon
361296465Sdelphij    if (bcont)
362296465Sdelphij        *bcont = NULL;
363183234Ssimon
364296465Sdelphij    if (!(headers = mime_parse_hdr(bio))) {
365296465Sdelphij        ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_MIME_PARSE_ERROR);
366296465Sdelphij        return NULL;
367296465Sdelphij    }
368183234Ssimon
369296465Sdelphij    if (!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) {
370296465Sdelphij        sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
371296465Sdelphij        ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_CONTENT_TYPE);
372296465Sdelphij        return NULL;
373296465Sdelphij    }
374183234Ssimon
375296465Sdelphij    /* Handle multipart/signed */
376183234Ssimon
377296465Sdelphij    if (!strcmp(hdr->value, "multipart/signed")) {
378296465Sdelphij        /* Split into two parts */
379296465Sdelphij        prm = mime_param_find(hdr, "boundary");
380296465Sdelphij        if (!prm || !prm->param_value) {
381296465Sdelphij            sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
382296465Sdelphij            ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_MULTIPART_BOUNDARY);
383296465Sdelphij            return NULL;
384296465Sdelphij        }
385296465Sdelphij        ret = multi_split(bio, prm->param_value, &parts);
386296465Sdelphij        sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
387296465Sdelphij        if (!ret || (sk_BIO_num(parts) != 2)) {
388296465Sdelphij            ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_MULTIPART_BODY_FAILURE);
389296465Sdelphij            sk_BIO_pop_free(parts, BIO_vfree);
390296465Sdelphij            return NULL;
391296465Sdelphij        }
392183234Ssimon
393296465Sdelphij        /* Parse the signature piece */
394296465Sdelphij        asnin = sk_BIO_value(parts, 1);
395183234Ssimon
396296465Sdelphij        if (!(headers = mime_parse_hdr(asnin))) {
397296465Sdelphij            ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_MIME_SIG_PARSE_ERROR);
398296465Sdelphij            sk_BIO_pop_free(parts, BIO_vfree);
399296465Sdelphij            return NULL;
400296465Sdelphij        }
401183234Ssimon
402296465Sdelphij        /* Get content type */
403183234Ssimon
404296465Sdelphij        if (!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) {
405296465Sdelphij            sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
406296465Sdelphij            ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_SIG_CONTENT_TYPE);
407296465Sdelphij            return NULL;
408296465Sdelphij        }
409183234Ssimon
410296465Sdelphij        if (strcmp(hdr->value, "application/x-pkcs7-signature") &&
411296465Sdelphij            strcmp(hdr->value, "application/pkcs7-signature")) {
412296465Sdelphij            ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_SIG_INVALID_MIME_TYPE);
413296465Sdelphij            ERR_add_error_data(2, "type: ", hdr->value);
414296465Sdelphij            sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
415296465Sdelphij            sk_BIO_pop_free(parts, BIO_vfree);
416296465Sdelphij            return NULL;
417296465Sdelphij        }
418296465Sdelphij        sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
419296465Sdelphij        /* Read in ASN1 */
420296465Sdelphij        if (!(val = b64_read_asn1(asnin, it))) {
421296465Sdelphij            ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_ASN1_SIG_PARSE_ERROR);
422296465Sdelphij            sk_BIO_pop_free(parts, BIO_vfree);
423296465Sdelphij            return NULL;
424296465Sdelphij        }
425183234Ssimon
426296465Sdelphij        if (bcont) {
427296465Sdelphij            *bcont = sk_BIO_value(parts, 0);
428296465Sdelphij            BIO_free(asnin);
429296465Sdelphij            sk_BIO_free(parts);
430296465Sdelphij        } else
431296465Sdelphij            sk_BIO_pop_free(parts, BIO_vfree);
432296465Sdelphij        return val;
433296465Sdelphij    }
434183234Ssimon
435296465Sdelphij    /* OK, if not multipart/signed try opaque signature */
436183234Ssimon
437296465Sdelphij    if (strcmp(hdr->value, "application/x-pkcs7-mime") &&
438296465Sdelphij        strcmp(hdr->value, "application/pkcs7-mime")) {
439296465Sdelphij        ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_INVALID_MIME_TYPE);
440296465Sdelphij        ERR_add_error_data(2, "type: ", hdr->value);
441296465Sdelphij        sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
442296465Sdelphij        return NULL;
443296465Sdelphij    }
444183234Ssimon
445296465Sdelphij    sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
446296465Sdelphij
447296465Sdelphij    if (!(val = b64_read_asn1(bio, it))) {
448296465Sdelphij        ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_ASN1_PARSE_ERROR);
449296465Sdelphij        return NULL;
450296465Sdelphij    }
451296465Sdelphij    return val;
452296465Sdelphij
453183234Ssimon}
454183234Ssimon
455183234Ssimon/* Copy text from one BIO to another making the output CRLF at EOL */
456183234Ssimonint SMIME_crlf_copy(BIO *in, BIO *out, int flags)
457183234Ssimon{
458296465Sdelphij    BIO *bf;
459296465Sdelphij    char eol;
460296465Sdelphij    int len;
461296465Sdelphij    char linebuf[MAX_SMLEN];
462296465Sdelphij    /*
463296465Sdelphij     * Buffer output so we don't write one line at a time. This is useful
464296465Sdelphij     * when streaming as we don't end up with one OCTET STRING per line.
465296465Sdelphij     */
466296465Sdelphij    bf = BIO_new(BIO_f_buffer());
467296465Sdelphij    if (!bf)
468296465Sdelphij        return 0;
469296465Sdelphij    out = BIO_push(bf, out);
470296465Sdelphij    if (flags & SMIME_BINARY) {
471296465Sdelphij        while ((len = BIO_read(in, linebuf, MAX_SMLEN)) > 0)
472296465Sdelphij            BIO_write(out, linebuf, len);
473296465Sdelphij    } else {
474296465Sdelphij        if (flags & SMIME_TEXT)
475296465Sdelphij            BIO_printf(out, "Content-Type: text/plain\r\n\r\n");
476296465Sdelphij        while ((len = BIO_gets(in, linebuf, MAX_SMLEN)) > 0) {
477296465Sdelphij            eol = strip_eol(linebuf, &len);
478296465Sdelphij            if (len)
479296465Sdelphij                BIO_write(out, linebuf, len);
480296465Sdelphij            if (eol)
481296465Sdelphij                BIO_write(out, "\r\n", 2);
482296465Sdelphij        }
483296465Sdelphij    }
484296465Sdelphij    (void)BIO_flush(out);
485296465Sdelphij    BIO_pop(out);
486296465Sdelphij    BIO_free(bf);
487296465Sdelphij    return 1;
488183234Ssimon}
489183234Ssimon
490183234Ssimon/* Strip off headers if they are text/plain */
491183234Ssimonint SMIME_text(BIO *in, BIO *out)
492183234Ssimon{
493296465Sdelphij    char iobuf[4096];
494296465Sdelphij    int len;
495296465Sdelphij    STACK_OF(MIME_HEADER) *headers;
496296465Sdelphij    MIME_HEADER *hdr;
497183234Ssimon
498296465Sdelphij    if (!(headers = mime_parse_hdr(in))) {
499296465Sdelphij        ASN1err(ASN1_F_SMIME_TEXT, ASN1_R_MIME_PARSE_ERROR);
500296465Sdelphij        return 0;
501296465Sdelphij    }
502296465Sdelphij    if (!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) {
503296465Sdelphij        ASN1err(ASN1_F_SMIME_TEXT, ASN1_R_MIME_NO_CONTENT_TYPE);
504296465Sdelphij        sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
505296465Sdelphij        return 0;
506296465Sdelphij    }
507296465Sdelphij    if (strcmp(hdr->value, "text/plain")) {
508296465Sdelphij        ASN1err(ASN1_F_SMIME_TEXT, ASN1_R_INVALID_MIME_TYPE);
509296465Sdelphij        ERR_add_error_data(2, "type: ", hdr->value);
510296465Sdelphij        sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
511296465Sdelphij        return 0;
512296465Sdelphij    }
513296465Sdelphij    sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
514296465Sdelphij    while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
515296465Sdelphij        BIO_write(out, iobuf, len);
516296465Sdelphij    if (len < 0)
517296465Sdelphij        return 0;
518296465Sdelphij    return 1;
519183234Ssimon}
520183234Ssimon
521296465Sdelphij/*
522296465Sdelphij * Split a multipart/XXX message body into component parts: result is
523183234Ssimon * canonical parts in a STACK of bios
524183234Ssimon */
525183234Ssimon
526183234Ssimonstatic int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret)
527183234Ssimon{
528296465Sdelphij    char linebuf[MAX_SMLEN];
529296465Sdelphij    int len, blen;
530296465Sdelphij    int eol = 0, next_eol = 0;
531296465Sdelphij    BIO *bpart = NULL;
532296465Sdelphij    STACK_OF(BIO) *parts;
533296465Sdelphij    char state, part, first;
534183234Ssimon
535296465Sdelphij    blen = strlen(bound);
536296465Sdelphij    part = 0;
537296465Sdelphij    state = 0;
538296465Sdelphij    first = 1;
539296465Sdelphij    parts = sk_BIO_new_null();
540296465Sdelphij    *ret = parts;
541296465Sdelphij    while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) {
542296465Sdelphij        state = mime_bound_check(linebuf, len, bound, blen);
543296465Sdelphij        if (state == 1) {
544296465Sdelphij            first = 1;
545296465Sdelphij            part++;
546296465Sdelphij        } else if (state == 2) {
547296465Sdelphij            sk_BIO_push(parts, bpart);
548296465Sdelphij            return 1;
549296465Sdelphij        } else if (part) {
550296465Sdelphij            /* Strip CR+LF from linebuf */
551296465Sdelphij            next_eol = strip_eol(linebuf, &len);
552296465Sdelphij            if (first) {
553296465Sdelphij                first = 0;
554296465Sdelphij                if (bpart)
555296465Sdelphij                    sk_BIO_push(parts, bpart);
556296465Sdelphij                bpart = BIO_new(BIO_s_mem());
557296465Sdelphij                BIO_set_mem_eof_return(bpart, 0);
558296465Sdelphij            } else if (eol)
559296465Sdelphij                BIO_write(bpart, "\r\n", 2);
560296465Sdelphij            eol = next_eol;
561296465Sdelphij            if (len)
562296465Sdelphij                BIO_write(bpart, linebuf, len);
563296465Sdelphij        }
564296465Sdelphij    }
565296465Sdelphij    return 0;
566183234Ssimon}
567183234Ssimon
568183234Ssimon/* This is the big one: parse MIME header lines up to message body */
569183234Ssimon
570296465Sdelphij#define MIME_INVALID    0
571296465Sdelphij#define MIME_START      1
572296465Sdelphij#define MIME_TYPE       2
573296465Sdelphij#define MIME_NAME       3
574296465Sdelphij#define MIME_VALUE      4
575296465Sdelphij#define MIME_QUOTE      5
576296465Sdelphij#define MIME_COMMENT    6
577183234Ssimon
578183234Ssimonstatic STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio)
579183234Ssimon{
580296465Sdelphij    char *p, *q, c;
581296465Sdelphij    char *ntmp;
582296465Sdelphij    char linebuf[MAX_SMLEN];
583296465Sdelphij    MIME_HEADER *mhdr = NULL;
584296465Sdelphij    STACK_OF(MIME_HEADER) *headers;
585296465Sdelphij    int len, state, save_state = 0;
586183234Ssimon
587296465Sdelphij    headers = sk_MIME_HEADER_new(mime_hdr_cmp);
588296465Sdelphij    if (!headers)
589296465Sdelphij        return NULL;
590296465Sdelphij    while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) {
591296465Sdelphij        /* If whitespace at line start then continuation line */
592296465Sdelphij        if (mhdr && isspace((unsigned char)linebuf[0]))
593296465Sdelphij            state = MIME_NAME;
594296465Sdelphij        else
595296465Sdelphij            state = MIME_START;
596296465Sdelphij        ntmp = NULL;
597296465Sdelphij        /* Go through all characters */
598296465Sdelphij        for (p = linebuf, q = linebuf; (c = *p) && (c != '\r') && (c != '\n');
599296465Sdelphij             p++) {
600183234Ssimon
601296465Sdelphij            /*
602296465Sdelphij             * State machine to handle MIME headers if this looks horrible
603296465Sdelphij             * that's because it *is*
604296465Sdelphij             */
605183234Ssimon
606296465Sdelphij            switch (state) {
607296465Sdelphij            case MIME_START:
608296465Sdelphij                if (c == ':') {
609296465Sdelphij                    state = MIME_TYPE;
610296465Sdelphij                    *p = 0;
611296465Sdelphij                    ntmp = strip_ends(q);
612296465Sdelphij                    q = p + 1;
613296465Sdelphij                }
614296465Sdelphij                break;
615183234Ssimon
616296465Sdelphij            case MIME_TYPE:
617296465Sdelphij                if (c == ';') {
618296465Sdelphij                    mime_debug("Found End Value\n");
619296465Sdelphij                    *p = 0;
620296465Sdelphij                    mhdr = mime_hdr_new(ntmp, strip_ends(q));
621296465Sdelphij                    sk_MIME_HEADER_push(headers, mhdr);
622296465Sdelphij                    ntmp = NULL;
623296465Sdelphij                    q = p + 1;
624296465Sdelphij                    state = MIME_NAME;
625296465Sdelphij                } else if (c == '(') {
626296465Sdelphij                    save_state = state;
627296465Sdelphij                    state = MIME_COMMENT;
628296465Sdelphij                }
629296465Sdelphij                break;
630183234Ssimon
631296465Sdelphij            case MIME_COMMENT:
632296465Sdelphij                if (c == ')') {
633296465Sdelphij                    state = save_state;
634296465Sdelphij                }
635296465Sdelphij                break;
636183234Ssimon
637296465Sdelphij            case MIME_NAME:
638296465Sdelphij                if (c == '=') {
639296465Sdelphij                    state = MIME_VALUE;
640296465Sdelphij                    *p = 0;
641296465Sdelphij                    ntmp = strip_ends(q);
642296465Sdelphij                    q = p + 1;
643296465Sdelphij                }
644296465Sdelphij                break;
645183234Ssimon
646296465Sdelphij            case MIME_VALUE:
647296465Sdelphij                if (c == ';') {
648296465Sdelphij                    state = MIME_NAME;
649296465Sdelphij                    *p = 0;
650296465Sdelphij                    mime_hdr_addparam(mhdr, ntmp, strip_ends(q));
651296465Sdelphij                    ntmp = NULL;
652296465Sdelphij                    q = p + 1;
653296465Sdelphij                } else if (c == '"') {
654296465Sdelphij                    mime_debug("Found Quote\n");
655296465Sdelphij                    state = MIME_QUOTE;
656296465Sdelphij                } else if (c == '(') {
657296465Sdelphij                    save_state = state;
658296465Sdelphij                    state = MIME_COMMENT;
659296465Sdelphij                }
660296465Sdelphij                break;
661183234Ssimon
662296465Sdelphij            case MIME_QUOTE:
663296465Sdelphij                if (c == '"') {
664296465Sdelphij                    mime_debug("Found Match Quote\n");
665296465Sdelphij                    state = MIME_VALUE;
666296465Sdelphij                }
667296465Sdelphij                break;
668296465Sdelphij            }
669296465Sdelphij        }
670183234Ssimon
671296465Sdelphij        if (state == MIME_TYPE) {
672296465Sdelphij            mhdr = mime_hdr_new(ntmp, strip_ends(q));
673296465Sdelphij            sk_MIME_HEADER_push(headers, mhdr);
674296465Sdelphij        } else if (state == MIME_VALUE)
675296465Sdelphij            mime_hdr_addparam(mhdr, ntmp, strip_ends(q));
676296465Sdelphij        if (p == linebuf)
677296465Sdelphij            break;              /* Blank line means end of headers */
678296465Sdelphij    }
679183234Ssimon
680296465Sdelphij    return headers;
681183234Ssimon
682183234Ssimon}
683183234Ssimon
684183234Ssimonstatic char *strip_ends(char *name)
685183234Ssimon{
686296465Sdelphij    return strip_end(strip_start(name));
687183234Ssimon}
688183234Ssimon
689183234Ssimon/* Strip a parameter of whitespace from start of param */
690183234Ssimonstatic char *strip_start(char *name)
691183234Ssimon{
692296465Sdelphij    char *p, c;
693296465Sdelphij    /* Look for first non white space or quote */
694296465Sdelphij    for (p = name; (c = *p); p++) {
695296465Sdelphij        if (c == '"') {
696296465Sdelphij            /* Next char is start of string if non null */
697296465Sdelphij            if (p[1])
698296465Sdelphij                return p + 1;
699296465Sdelphij            /* Else null string */
700296465Sdelphij            return NULL;
701296465Sdelphij        }
702296465Sdelphij        if (!isspace((unsigned char)c))
703296465Sdelphij            return p;
704296465Sdelphij    }
705296465Sdelphij    return NULL;
706183234Ssimon}
707183234Ssimon
708183234Ssimon/* As above but strip from end of string : maybe should handle brackets? */
709183234Ssimonstatic char *strip_end(char *name)
710183234Ssimon{
711296465Sdelphij    char *p, c;
712296465Sdelphij    if (!name)
713296465Sdelphij        return NULL;
714296465Sdelphij    /* Look for first non white space or quote */
715296465Sdelphij    for (p = name + strlen(name) - 1; p >= name; p--) {
716296465Sdelphij        c = *p;
717296465Sdelphij        if (c == '"') {
718296465Sdelphij            if (p - 1 == name)
719296465Sdelphij                return NULL;
720296465Sdelphij            *p = 0;
721296465Sdelphij            return name;
722296465Sdelphij        }
723296465Sdelphij        if (isspace((unsigned char)c))
724296465Sdelphij            *p = 0;
725296465Sdelphij        else
726296465Sdelphij            return name;
727296465Sdelphij    }
728296465Sdelphij    return NULL;
729183234Ssimon}
730183234Ssimon
731183234Ssimonstatic MIME_HEADER *mime_hdr_new(char *name, char *value)
732183234Ssimon{
733296465Sdelphij    MIME_HEADER *mhdr;
734296465Sdelphij    char *tmpname, *tmpval, *p;
735296465Sdelphij    int c;
736296465Sdelphij    if (name) {
737296465Sdelphij        if (!(tmpname = BUF_strdup(name)))
738296465Sdelphij            return NULL;
739296465Sdelphij        for (p = tmpname; *p; p++) {
740296465Sdelphij            c = *p;
741296465Sdelphij            if (isupper(c)) {
742296465Sdelphij                c = tolower(c);
743296465Sdelphij                *p = c;
744296465Sdelphij            }
745296465Sdelphij        }
746296465Sdelphij    } else
747296465Sdelphij        tmpname = NULL;
748296465Sdelphij    if (value) {
749296465Sdelphij        if (!(tmpval = BUF_strdup(value)))
750296465Sdelphij            return NULL;
751296465Sdelphij        for (p = tmpval; *p; p++) {
752296465Sdelphij            c = *p;
753296465Sdelphij            if (isupper(c)) {
754296465Sdelphij                c = tolower(c);
755296465Sdelphij                *p = c;
756296465Sdelphij            }
757296465Sdelphij        }
758296465Sdelphij    } else
759296465Sdelphij        tmpval = NULL;
760296465Sdelphij    mhdr = (MIME_HEADER *)OPENSSL_malloc(sizeof(MIME_HEADER));
761296465Sdelphij    if (!mhdr)
762296465Sdelphij        return NULL;
763296465Sdelphij    mhdr->name = tmpname;
764296465Sdelphij    mhdr->value = tmpval;
765296465Sdelphij    if (!(mhdr->params = sk_MIME_PARAM_new(mime_param_cmp)))
766296465Sdelphij        return NULL;
767296465Sdelphij    return mhdr;
768183234Ssimon}
769296465Sdelphij
770183234Ssimonstatic int mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value)
771183234Ssimon{
772296465Sdelphij    char *tmpname, *tmpval, *p;
773296465Sdelphij    int c;
774296465Sdelphij    MIME_PARAM *mparam;
775296465Sdelphij    if (name) {
776296465Sdelphij        tmpname = BUF_strdup(name);
777296465Sdelphij        if (!tmpname)
778296465Sdelphij            return 0;
779296465Sdelphij        for (p = tmpname; *p; p++) {
780296465Sdelphij            c = *p;
781296465Sdelphij            if (isupper(c)) {
782296465Sdelphij                c = tolower(c);
783296465Sdelphij                *p = c;
784296465Sdelphij            }
785296465Sdelphij        }
786296465Sdelphij    } else
787296465Sdelphij        tmpname = NULL;
788296465Sdelphij    if (value) {
789296465Sdelphij        tmpval = BUF_strdup(value);
790296465Sdelphij        if (!tmpval)
791296465Sdelphij            return 0;
792296465Sdelphij    } else
793296465Sdelphij        tmpval = NULL;
794296465Sdelphij    /* Parameter values are case sensitive so leave as is */
795296465Sdelphij    mparam = (MIME_PARAM *)OPENSSL_malloc(sizeof(MIME_PARAM));
796296465Sdelphij    if (!mparam)
797296465Sdelphij        return 0;
798296465Sdelphij    mparam->param_name = tmpname;
799296465Sdelphij    mparam->param_value = tmpval;
800296465Sdelphij    sk_MIME_PARAM_push(mhdr->params, mparam);
801296465Sdelphij    return 1;
802183234Ssimon}
803183234Ssimon
804296465Sdelphijstatic int mime_hdr_cmp(const MIME_HEADER *const *a,
805296465Sdelphij                        const MIME_HEADER *const *b)
806183234Ssimon{
807296465Sdelphij    if (!(*a)->name || !(*b)->name)
808296465Sdelphij        return ! !(*a)->name - ! !(*b)->name;
809237998Sjkim
810296465Sdelphij    return (strcmp((*a)->name, (*b)->name));
811183234Ssimon}
812183234Ssimon
813296465Sdelphijstatic int mime_param_cmp(const MIME_PARAM *const *a,
814296465Sdelphij                          const MIME_PARAM *const *b)
815183234Ssimon{
816296465Sdelphij    if (!(*a)->param_name || !(*b)->param_name)
817296465Sdelphij        return ! !(*a)->param_name - ! !(*b)->param_name;
818296465Sdelphij    return (strcmp((*a)->param_name, (*b)->param_name));
819183234Ssimon}
820183234Ssimon
821183234Ssimon/* Find a header with a given name (if possible) */
822183234Ssimon
823183234Ssimonstatic MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name)
824183234Ssimon{
825296465Sdelphij    MIME_HEADER htmp;
826296465Sdelphij    int idx;
827296465Sdelphij    htmp.name = name;
828296465Sdelphij    idx = sk_MIME_HEADER_find(hdrs, &htmp);
829296465Sdelphij    if (idx < 0)
830296465Sdelphij        return NULL;
831296465Sdelphij    return sk_MIME_HEADER_value(hdrs, idx);
832183234Ssimon}
833183234Ssimon
834183234Ssimonstatic MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name)
835183234Ssimon{
836296465Sdelphij    MIME_PARAM param;
837296465Sdelphij    int idx;
838296465Sdelphij    param.param_name = name;
839296465Sdelphij    idx = sk_MIME_PARAM_find(hdr->params, &param);
840296465Sdelphij    if (idx < 0)
841296465Sdelphij        return NULL;
842296465Sdelphij    return sk_MIME_PARAM_value(hdr->params, idx);
843183234Ssimon}
844183234Ssimon
845183234Ssimonstatic void mime_hdr_free(MIME_HEADER *hdr)
846183234Ssimon{
847296465Sdelphij    if (hdr->name)
848296465Sdelphij        OPENSSL_free(hdr->name);
849296465Sdelphij    if (hdr->value)
850296465Sdelphij        OPENSSL_free(hdr->value);
851296465Sdelphij    if (hdr->params)
852296465Sdelphij        sk_MIME_PARAM_pop_free(hdr->params, mime_param_free);
853296465Sdelphij    OPENSSL_free(hdr);
854183234Ssimon}
855183234Ssimon
856183234Ssimonstatic void mime_param_free(MIME_PARAM *param)
857183234Ssimon{
858296465Sdelphij    if (param->param_name)
859296465Sdelphij        OPENSSL_free(param->param_name);
860296465Sdelphij    if (param->param_value)
861296465Sdelphij        OPENSSL_free(param->param_value);
862296465Sdelphij    OPENSSL_free(param);
863183234Ssimon}
864183234Ssimon
865296465Sdelphij/*-
866296465Sdelphij * Check for a multipart boundary. Returns:
867183234Ssimon * 0 : no boundary
868183234Ssimon * 1 : part boundary
869183234Ssimon * 2 : final boundary
870183234Ssimon */
871183234Ssimonstatic int mime_bound_check(char *line, int linelen, char *bound, int blen)
872183234Ssimon{
873296465Sdelphij    if (linelen == -1)
874296465Sdelphij        linelen = strlen(line);
875296465Sdelphij    if (blen == -1)
876296465Sdelphij        blen = strlen(bound);
877296465Sdelphij    /* Quickly eliminate if line length too short */
878296465Sdelphij    if (blen + 2 > linelen)
879296465Sdelphij        return 0;
880296465Sdelphij    /* Check for part boundary */
881296465Sdelphij    if (!strncmp(line, "--", 2) && !strncmp(line + 2, bound, blen)) {
882296465Sdelphij        if (!strncmp(line + blen + 2, "--", 2))
883296465Sdelphij            return 2;
884296465Sdelphij        else
885296465Sdelphij            return 1;
886296465Sdelphij    }
887296465Sdelphij    return 0;
888183234Ssimon}
889183234Ssimon
890183234Ssimonstatic int strip_eol(char *linebuf, int *plen)
891296465Sdelphij{
892296465Sdelphij    int len = *plen;
893296465Sdelphij    char *p, c;
894296465Sdelphij    int is_eol = 0;
895296465Sdelphij    p = linebuf + len - 1;
896296465Sdelphij    for (p = linebuf + len - 1; len > 0; len--, p--) {
897296465Sdelphij        c = *p;
898296465Sdelphij        if (c == '\n')
899296465Sdelphij            is_eol = 1;
900296465Sdelphij        else if (c != '\r')
901296465Sdelphij            break;
902296465Sdelphij    }
903296465Sdelphij    *plen = len;
904296465Sdelphij    return is_eol;
905296465Sdelphij}
906