github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/overlord/configstate/configcore/certs.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  // +build !nomanagers
     3  
     4  /*
     5   * Copyright (C) 2020 Canonical Ltd
     6   *
     7   * This program is free software: you can redistribute it and/or modify
     8   * it under the terms of the GNU General Public License version 3 as
     9   * published by the Free Software Foundation.
    10   *
    11   * This program is distributed in the hope that it will be useful,
    12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14   * GNU General Public License for more details.
    15   *
    16   * You should have received a copy of the GNU General Public License
    17   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18   *
    19   */
    20  
    21  package configcore
    22  
    23  import (
    24  	"crypto/x509"
    25  	"fmt"
    26  	"io/ioutil"
    27  	"os"
    28  	"path/filepath"
    29  	"strings"
    30  
    31  	"github.com/snapcore/snapd/dirs"
    32  	"github.com/snapcore/snapd/overlord/configstate/config"
    33  )
    34  
    35  func handleCertConfiguration(tr config.Conf, opts *fsOnlyContext) error {
    36  	// This handles the "snap revert core" case:
    37  	// We need to go over each pem cert on disk and check if there is
    38  	// a matching config entry - if not->delete the cert
    39  	//
    40  	// XXX: remove this code once we have a general way to handle
    41  	//      "snap revert" and config updates
    42  	//
    43  	// TODO: add ways to detect cleanly if tr is a patch, skip the sync code if it is
    44  	storeCerts, err := filepath.Glob(filepath.Join(dirs.SnapdStoreSSLCertsDir, "*.pem"))
    45  	if err != nil {
    46  		return fmt.Errorf("cannot get exiting store certs: %v", err)
    47  	}
    48  	for _, storeCertPath := range storeCerts {
    49  		optionName := strings.TrimSuffix(filepath.Base(storeCertPath), ".pem")
    50  		v, err := coreCfg(tr, "store-certs."+optionName)
    51  		if err != nil {
    52  			return err
    53  		}
    54  		if v == "" {
    55  			if err := os.Remove(storeCertPath); err != nil {
    56  				return err
    57  			}
    58  		}
    59  	}
    60  
    61  	// add/remove regular (non revert) changes
    62  	for _, name := range tr.Changes() {
    63  		if !strings.HasPrefix(name, "core.store-certs.") {
    64  			continue
    65  		}
    66  
    67  		nameWithoutSnap := strings.SplitN(name, ".", 2)[1]
    68  		cert, err := coreCfg(tr, nameWithoutSnap)
    69  		if err != nil {
    70  			return fmt.Errorf("internal error: cannot get data for %s: %v", nameWithoutSnap, err)
    71  		}
    72  		optionName := strings.SplitN(name, ".", 3)[2]
    73  		certPath := filepath.Join(dirs.SnapdStoreSSLCertsDir, optionName+".pem")
    74  		switch cert {
    75  		case "":
    76  			// remove
    77  			if err := os.Remove(certPath); err != nil && !os.IsNotExist(err) {
    78  				return fmt.Errorf("cannot remove store certificate: %v", err)
    79  			}
    80  		default:
    81  			if err := os.MkdirAll(dirs.SnapdStoreSSLCertsDir, 0755); err != nil {
    82  				return fmt.Errorf("cannot create store ssl certs dir: %v", err)
    83  			}
    84  			if err := ioutil.WriteFile(certPath, []byte(cert), 0644); err != nil {
    85  				return fmt.Errorf("cannot write store certificate: %v", err)
    86  			}
    87  		}
    88  	}
    89  
    90  	return nil
    91  }
    92  
    93  func validateCertSettings(tr config.Conf) error {
    94  	for _, name := range tr.Changes() {
    95  		if !strings.HasPrefix(name, "core.store-certs.") {
    96  			continue
    97  		}
    98  
    99  		nameWithoutSnap := strings.SplitN(name, ".", 2)[1]
   100  		cert, err := coreCfg(tr, nameWithoutSnap)
   101  		if err != nil {
   102  			return fmt.Errorf("internal error: cannot get data for %s: %v", nameWithoutSnap, err)
   103  		}
   104  		if cert != "" {
   105  			optionName := strings.SplitN(name, ".", 3)[2]
   106  			if !validCertName(optionName) {
   107  				return fmt.Errorf("cannot set store ssl certificate under name %q: name must only contain word characters or a dash", optionName)
   108  			}
   109  			cp := x509.NewCertPool()
   110  			if !cp.AppendCertsFromPEM([]byte(cert)) {
   111  				return fmt.Errorf("cannot decode pem certificate %q", optionName)
   112  			}
   113  		}
   114  	}
   115  
   116  	return nil
   117  }