github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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 }