1e1051a39Sopenharmony_ci/*
2e1051a39Sopenharmony_ci * Copyright 1999-2020 The OpenSSL Project Authors. All Rights Reserved.
3e1051a39Sopenharmony_ci *
4e1051a39Sopenharmony_ci * Licensed under the Apache License 2.0 (the "License").  You may not use
5e1051a39Sopenharmony_ci * this file except in compliance with the License.  You can obtain a copy
6e1051a39Sopenharmony_ci * in the file LICENSE in the source distribution or at
7e1051a39Sopenharmony_ci * https://www.openssl.org/source/license.html
8e1051a39Sopenharmony_ci */
9e1051a39Sopenharmony_ci
10e1051a39Sopenharmony_ci#include <stdio.h>
11e1051a39Sopenharmony_ci#include <stdlib.h>
12e1051a39Sopenharmony_ci#include <string.h>
13e1051a39Sopenharmony_ci#include <openssl/pem.h>
14e1051a39Sopenharmony_ci#include <openssl/err.h>
15e1051a39Sopenharmony_ci#include <openssl/pkcs12.h>
16e1051a39Sopenharmony_ci#include "p12_local.h"
17e1051a39Sopenharmony_ci
18e1051a39Sopenharmony_ci/* PKCS#12 password change routine */
19e1051a39Sopenharmony_ci
20e1051a39Sopenharmony_cistatic int newpass_p12(PKCS12 *p12, const char *oldpass, const char *newpass);
21e1051a39Sopenharmony_cistatic int newpass_bags(STACK_OF(PKCS12_SAFEBAG) *bags, const char *oldpass,
22e1051a39Sopenharmony_ci                        const char *newpass);
23e1051a39Sopenharmony_cistatic int newpass_bag(PKCS12_SAFEBAG *bag, const char *oldpass,
24e1051a39Sopenharmony_ci                        const char *newpass);
25e1051a39Sopenharmony_cistatic int alg_get(const X509_ALGOR *alg, int *pnid, int *piter,
26e1051a39Sopenharmony_ci                   int *psaltlen);
27e1051a39Sopenharmony_ci
28e1051a39Sopenharmony_ci/*
29e1051a39Sopenharmony_ci * Change the password on a PKCS#12 structure.
30e1051a39Sopenharmony_ci */
31e1051a39Sopenharmony_ci
32e1051a39Sopenharmony_ciint PKCS12_newpass(PKCS12 *p12, const char *oldpass, const char *newpass)
33e1051a39Sopenharmony_ci{
34e1051a39Sopenharmony_ci    /* Check for NULL PKCS12 structure */
35e1051a39Sopenharmony_ci
36e1051a39Sopenharmony_ci    if (p12 == NULL) {
37e1051a39Sopenharmony_ci        ERR_raise(ERR_LIB_PKCS12, PKCS12_R_INVALID_NULL_PKCS12_POINTER);
38e1051a39Sopenharmony_ci        return 0;
39e1051a39Sopenharmony_ci    }
40e1051a39Sopenharmony_ci
41e1051a39Sopenharmony_ci    /* Check the mac */
42e1051a39Sopenharmony_ci
43e1051a39Sopenharmony_ci    if (!PKCS12_verify_mac(p12, oldpass, -1)) {
44e1051a39Sopenharmony_ci        ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_VERIFY_FAILURE);
45e1051a39Sopenharmony_ci        return 0;
46e1051a39Sopenharmony_ci    }
47e1051a39Sopenharmony_ci
48e1051a39Sopenharmony_ci    if (!newpass_p12(p12, oldpass, newpass)) {
49e1051a39Sopenharmony_ci        ERR_raise(ERR_LIB_PKCS12, PKCS12_R_PARSE_ERROR);
50e1051a39Sopenharmony_ci        return 0;
51e1051a39Sopenharmony_ci    }
52e1051a39Sopenharmony_ci
53e1051a39Sopenharmony_ci    return 1;
54e1051a39Sopenharmony_ci}
55e1051a39Sopenharmony_ci
56e1051a39Sopenharmony_ci/* Parse the outer PKCS#12 structure */
57e1051a39Sopenharmony_ci
58e1051a39Sopenharmony_cistatic int newpass_p12(PKCS12 *p12, const char *oldpass, const char *newpass)
59e1051a39Sopenharmony_ci{
60e1051a39Sopenharmony_ci    STACK_OF(PKCS7) *asafes = NULL, *newsafes = NULL;
61e1051a39Sopenharmony_ci    STACK_OF(PKCS12_SAFEBAG) *bags = NULL;
62e1051a39Sopenharmony_ci    int i, bagnid, pbe_nid = 0, pbe_iter = 0, pbe_saltlen = 0;
63e1051a39Sopenharmony_ci    PKCS7 *p7, *p7new;
64e1051a39Sopenharmony_ci    ASN1_OCTET_STRING *p12_data_tmp = NULL, *macoct = NULL;
65e1051a39Sopenharmony_ci    unsigned char mac[EVP_MAX_MD_SIZE];
66e1051a39Sopenharmony_ci    unsigned int maclen;
67e1051a39Sopenharmony_ci    int rv = 0;
68e1051a39Sopenharmony_ci
69e1051a39Sopenharmony_ci    if ((asafes = PKCS12_unpack_authsafes(p12)) == NULL)
70e1051a39Sopenharmony_ci        goto err;
71e1051a39Sopenharmony_ci    if ((newsafes = sk_PKCS7_new_null()) == NULL)
72e1051a39Sopenharmony_ci        goto err;
73e1051a39Sopenharmony_ci    for (i = 0; i < sk_PKCS7_num(asafes); i++) {
74e1051a39Sopenharmony_ci        p7 = sk_PKCS7_value(asafes, i);
75e1051a39Sopenharmony_ci        bagnid = OBJ_obj2nid(p7->type);
76e1051a39Sopenharmony_ci        if (bagnid == NID_pkcs7_data) {
77e1051a39Sopenharmony_ci            bags = PKCS12_unpack_p7data(p7);
78e1051a39Sopenharmony_ci        } else if (bagnid == NID_pkcs7_encrypted) {
79e1051a39Sopenharmony_ci            bags = PKCS12_unpack_p7encdata(p7, oldpass, -1);
80e1051a39Sopenharmony_ci            if (p7->d.encrypted == NULL
81e1051a39Sopenharmony_ci                    || !alg_get(p7->d.encrypted->enc_data->algorithm,
82e1051a39Sopenharmony_ci                                &pbe_nid, &pbe_iter, &pbe_saltlen))
83e1051a39Sopenharmony_ci                goto err;
84e1051a39Sopenharmony_ci        } else {
85e1051a39Sopenharmony_ci            continue;
86e1051a39Sopenharmony_ci        }
87e1051a39Sopenharmony_ci        if (bags == NULL)
88e1051a39Sopenharmony_ci            goto err;
89e1051a39Sopenharmony_ci        if (!newpass_bags(bags, oldpass, newpass))
90e1051a39Sopenharmony_ci            goto err;
91e1051a39Sopenharmony_ci        /* Repack bag in same form with new password */
92e1051a39Sopenharmony_ci        if (bagnid == NID_pkcs7_data)
93e1051a39Sopenharmony_ci            p7new = PKCS12_pack_p7data(bags);
94e1051a39Sopenharmony_ci        else
95e1051a39Sopenharmony_ci            p7new = PKCS12_pack_p7encdata(pbe_nid, newpass, -1, NULL,
96e1051a39Sopenharmony_ci                                          pbe_saltlen, pbe_iter, bags);
97e1051a39Sopenharmony_ci        if (p7new == NULL || !sk_PKCS7_push(newsafes, p7new))
98e1051a39Sopenharmony_ci            goto err;
99e1051a39Sopenharmony_ci        sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
100e1051a39Sopenharmony_ci        bags = NULL;
101e1051a39Sopenharmony_ci    }
102e1051a39Sopenharmony_ci
103e1051a39Sopenharmony_ci    /* Repack safe: save old safe in case of error */
104e1051a39Sopenharmony_ci
105e1051a39Sopenharmony_ci    p12_data_tmp = p12->authsafes->d.data;
106e1051a39Sopenharmony_ci    if ((p12->authsafes->d.data = ASN1_OCTET_STRING_new()) == NULL)
107e1051a39Sopenharmony_ci        goto err;
108e1051a39Sopenharmony_ci    if (!PKCS12_pack_authsafes(p12, newsafes))
109e1051a39Sopenharmony_ci        goto err;
110e1051a39Sopenharmony_ci
111e1051a39Sopenharmony_ci    if (!PKCS12_gen_mac(p12, newpass, -1, mac, &maclen))
112e1051a39Sopenharmony_ci        goto err;
113e1051a39Sopenharmony_ci    X509_SIG_getm(p12->mac->dinfo, NULL, &macoct);
114e1051a39Sopenharmony_ci    if (!ASN1_OCTET_STRING_set(macoct, mac, maclen))
115e1051a39Sopenharmony_ci        goto err;
116e1051a39Sopenharmony_ci
117e1051a39Sopenharmony_ci    rv = 1;
118e1051a39Sopenharmony_ci
119e1051a39Sopenharmony_cierr:
120e1051a39Sopenharmony_ci    /* Restore old safe if necessary */
121e1051a39Sopenharmony_ci    if (rv == 1) {
122e1051a39Sopenharmony_ci        ASN1_OCTET_STRING_free(p12_data_tmp);
123e1051a39Sopenharmony_ci    } else if (p12_data_tmp != NULL) {
124e1051a39Sopenharmony_ci        ASN1_OCTET_STRING_free(p12->authsafes->d.data);
125e1051a39Sopenharmony_ci        p12->authsafes->d.data = p12_data_tmp;
126e1051a39Sopenharmony_ci    }
127e1051a39Sopenharmony_ci    sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
128e1051a39Sopenharmony_ci    sk_PKCS7_pop_free(asafes, PKCS7_free);
129e1051a39Sopenharmony_ci    sk_PKCS7_pop_free(newsafes, PKCS7_free);
130e1051a39Sopenharmony_ci    return rv;
131e1051a39Sopenharmony_ci}
132e1051a39Sopenharmony_ci
133e1051a39Sopenharmony_cistatic int newpass_bags(STACK_OF(PKCS12_SAFEBAG) *bags, const char *oldpass,
134e1051a39Sopenharmony_ci                        const char *newpass)
135e1051a39Sopenharmony_ci{
136e1051a39Sopenharmony_ci    int i;
137e1051a39Sopenharmony_ci    for (i = 0; i < sk_PKCS12_SAFEBAG_num(bags); i++) {
138e1051a39Sopenharmony_ci        if (!newpass_bag(sk_PKCS12_SAFEBAG_value(bags, i), oldpass, newpass))
139e1051a39Sopenharmony_ci            return 0;
140e1051a39Sopenharmony_ci    }
141e1051a39Sopenharmony_ci    return 1;
142e1051a39Sopenharmony_ci}
143e1051a39Sopenharmony_ci
144e1051a39Sopenharmony_ci/* Change password of safebag: only needs handle shrouded keybags */
145e1051a39Sopenharmony_ci
146e1051a39Sopenharmony_cistatic int newpass_bag(PKCS12_SAFEBAG *bag, const char *oldpass,
147e1051a39Sopenharmony_ci                       const char *newpass)
148e1051a39Sopenharmony_ci{
149e1051a39Sopenharmony_ci    PKCS8_PRIV_KEY_INFO *p8;
150e1051a39Sopenharmony_ci    X509_SIG *p8new;
151e1051a39Sopenharmony_ci    int p8_nid, p8_saltlen, p8_iter;
152e1051a39Sopenharmony_ci    const X509_ALGOR *shalg;
153e1051a39Sopenharmony_ci
154e1051a39Sopenharmony_ci    if (PKCS12_SAFEBAG_get_nid(bag) != NID_pkcs8ShroudedKeyBag)
155e1051a39Sopenharmony_ci        return 1;
156e1051a39Sopenharmony_ci
157e1051a39Sopenharmony_ci    if ((p8 = PKCS8_decrypt(bag->value.shkeybag, oldpass, -1)) == NULL)
158e1051a39Sopenharmony_ci        return 0;
159e1051a39Sopenharmony_ci    X509_SIG_get0(bag->value.shkeybag, &shalg, NULL);
160e1051a39Sopenharmony_ci    if (!alg_get(shalg, &p8_nid, &p8_iter, &p8_saltlen)) {
161e1051a39Sopenharmony_ci        PKCS8_PRIV_KEY_INFO_free(p8);
162e1051a39Sopenharmony_ci        return 0;
163e1051a39Sopenharmony_ci    }
164e1051a39Sopenharmony_ci    p8new = PKCS8_encrypt(p8_nid, NULL, newpass, -1, NULL, p8_saltlen,
165e1051a39Sopenharmony_ci                          p8_iter, p8);
166e1051a39Sopenharmony_ci    PKCS8_PRIV_KEY_INFO_free(p8);
167e1051a39Sopenharmony_ci    if (p8new == NULL)
168e1051a39Sopenharmony_ci        return 0;
169e1051a39Sopenharmony_ci    X509_SIG_free(bag->value.shkeybag);
170e1051a39Sopenharmony_ci    bag->value.shkeybag = p8new;
171e1051a39Sopenharmony_ci    return 1;
172e1051a39Sopenharmony_ci}
173e1051a39Sopenharmony_ci
174e1051a39Sopenharmony_cistatic int alg_get(const X509_ALGOR *alg, int *pnid, int *piter,
175e1051a39Sopenharmony_ci                   int *psaltlen)
176e1051a39Sopenharmony_ci{
177e1051a39Sopenharmony_ci    PBEPARAM *pbe;
178e1051a39Sopenharmony_ci
179e1051a39Sopenharmony_ci    pbe = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(PBEPARAM), alg->parameter);
180e1051a39Sopenharmony_ci    if (pbe == NULL)
181e1051a39Sopenharmony_ci        return 0;
182e1051a39Sopenharmony_ci    *pnid = OBJ_obj2nid(alg->algorithm);
183e1051a39Sopenharmony_ci    *piter = ASN1_INTEGER_get(pbe->iter);
184e1051a39Sopenharmony_ci    *psaltlen = pbe->salt->length;
185e1051a39Sopenharmony_ci    PBEPARAM_free(pbe);
186e1051a39Sopenharmony_ci    return 1;
187e1051a39Sopenharmony_ci}
188