2019-06-01 21:58:20 +08:00
// Copyright 2018 The mkcert Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
2018-06-28 13:29:20 +08:00
package main
import (
2019-11-10 06:30:15 +08:00
"bytes"
2018-06-28 13:29:20 +08:00
"log"
"os"
"os/exec"
"path/filepath"
2018-07-04 08:48:31 +08:00
"runtime"
2018-06-28 13:29:20 +08:00
"strings"
)
var (
2018-07-04 10:26:37 +08:00
hasNSS bool
2018-06-28 13:29:20 +08:00
hasCertutil bool
certutilPath string
2019-06-01 23:01:09 +08:00
nssDBs = [ ] string {
filepath . Join ( os . Getenv ( "HOME" ) , ".pki/nssdb" ) ,
filepath . Join ( os . Getenv ( "HOME" ) , "snap/chromium/current/.pki/nssdb" ) , // Snapcraft
"/etc/pki/nssdb" , // CentOS 7
}
firefoxPaths = [ ] string {
2019-11-30 06:36:50 +08:00
"/usr/bin/firefox" ,
"/usr/bin/firefox-nightly" ,
"/usr/bin/firefox-developer-edition" ,
2022-04-26 01:55:19 +08:00
"/snap/firefox" ,
2019-11-30 06:36:50 +08:00
"/Applications/Firefox.app" ,
2020-10-26 07:24:53 +08:00
"/Applications/FirefoxDeveloperEdition.app" ,
2018-08-13 09:11:34 +08:00
"/Applications/Firefox Developer Edition.app" ,
2019-01-09 01:22:13 +08:00
"/Applications/Firefox Nightly.app" ,
2018-08-13 09:44:02 +08:00
"C:\\Program Files\\Mozilla Firefox" ,
2019-06-01 23:01:09 +08:00
}
)
func init ( ) {
allPaths := append ( append ( [ ] string { } , nssDBs ... ) , firefoxPaths ... )
for _ , path := range allPaths {
if pathExists ( path ) {
hasNSS = true
break
}
2018-08-13 09:11:34 +08:00
}
2018-06-28 13:29:20 +08:00
2018-07-04 10:26:37 +08:00
switch runtime . GOOS {
case "darwin" :
2019-08-17 06:13:31 +08:00
switch {
case binaryExists ( "certutil" ) :
2019-06-01 21:55:58 +08:00
certutilPath , _ = exec . LookPath ( "certutil" )
2019-08-17 06:13:31 +08:00
hasCertutil = true
case binaryExists ( "/usr/local/opt/nss/bin/certutil" ) :
// Check the default Homebrew path, to save executing Ruby. #135
certutilPath = "/usr/local/opt/nss/bin/certutil"
hasCertutil = true
default :
2019-06-01 21:55:58 +08:00
out , err := exec . Command ( "brew" , "--prefix" , "nss" ) . Output ( )
if err == nil {
certutilPath = filepath . Join ( strings . TrimSpace ( string ( out ) ) , "bin" , "certutil" )
hasCertutil = pathExists ( certutilPath )
2018-08-26 05:52:43 +08:00
}
2018-07-04 10:26:37 +08:00
}
case "linux" :
2019-06-01 21:55:58 +08:00
if hasCertutil = binaryExists ( "certutil" ) ; hasCertutil {
certutilPath , _ = exec . LookPath ( "certutil" )
}
2018-07-04 10:26:37 +08:00
}
2018-06-28 13:29:20 +08:00
}
2018-07-04 10:26:37 +08:00
func ( m * mkcert ) checkNSS ( ) bool {
2018-06-28 13:29:20 +08:00
if ! hasCertutil {
return false
}
success := true
2018-07-04 10:26:37 +08:00
if m . forEachNSSProfile ( func ( profile string ) {
2018-06-28 13:29:20 +08:00
err := exec . Command ( certutilPath , "-V" , "-d" , profile , "-u" , "L" , "-n" , m . caUniqueName ( ) ) . Run ( )
if err != nil {
success = false
}
} ) == 0 {
success = false
}
return success
}
2018-08-13 08:32:34 +08:00
func ( m * mkcert ) installNSS ( ) bool {
2018-07-04 10:26:37 +08:00
if m . forEachNSSProfile ( func ( profile string ) {
2018-06-28 13:29:20 +08:00
cmd := exec . Command ( certutilPath , "-A" , "-d" , profile , "-t" , "C,," , "-n" , m . caUniqueName ( ) , "-i" , filepath . Join ( m . CAROOT , rootName ) )
2019-11-10 06:30:15 +08:00
out , err := execCertutil ( cmd )
fatalIfCmdErr ( err , "certutil -A -d " + profile , out )
2018-06-28 13:29:20 +08:00
} ) == 0 {
2018-07-04 10:26:37 +08:00
log . Printf ( "ERROR: no %s security databases found" , NSSBrowsers )
2018-08-13 08:32:34 +08:00
return false
2018-06-28 13:29:20 +08:00
}
2018-07-04 10:26:37 +08:00
if ! m . checkNSS ( ) {
log . Printf ( "Installing in %s failed. Please report the issue with details about your environment at https://github.com/FiloSottile/mkcert/issues/new 👎" , NSSBrowsers )
log . Printf ( "Note that if you never started %s, you need to do that at least once." , NSSBrowsers )
2018-08-13 08:32:34 +08:00
return false
2018-06-28 13:29:20 +08:00
}
2018-08-13 08:32:34 +08:00
return true
2018-06-28 13:29:20 +08:00
}
2018-07-04 10:26:37 +08:00
func ( m * mkcert ) uninstallNSS ( ) {
m . forEachNSSProfile ( func ( profile string ) {
2018-06-28 13:29:20 +08:00
err := exec . Command ( certutilPath , "-V" , "-d" , profile , "-u" , "L" , "-n" , m . caUniqueName ( ) ) . Run ( )
if err != nil {
return
}
cmd := exec . Command ( certutilPath , "-D" , "-d" , profile , "-n" , m . caUniqueName ( ) )
2019-11-10 06:30:15 +08:00
out , err := execCertutil ( cmd )
fatalIfCmdErr ( err , "certutil -D -d " + profile , out )
2018-06-28 13:29:20 +08:00
} )
}
2019-11-10 06:30:15 +08:00
// execCertutil will execute a "certutil" command and if needed re-execute
// the command with commandWithSudo to work around file permissions.
func execCertutil ( cmd * exec . Cmd ) ( [ ] byte , error ) {
out , err := cmd . CombinedOutput ( )
if err != nil && bytes . Contains ( out , [ ] byte ( "SEC_ERROR_READ_ONLY" ) ) && runtime . GOOS != "windows" {
origArgs := cmd . Args [ 1 : ]
cmd = commandWithSudo ( cmd . Path )
cmd . Args = append ( cmd . Args , origArgs ... )
out , err = cmd . CombinedOutput ( )
}
return out , err
}
2018-07-04 10:26:37 +08:00
func ( m * mkcert ) forEachNSSProfile ( f func ( profile string ) ) ( found int ) {
2022-04-26 01:55:19 +08:00
var profiles [ ] string
2019-06-01 23:01:09 +08:00
profiles = append ( profiles , nssDBs ... )
2022-04-26 01:55:19 +08:00
for _ , ff := range FirefoxProfiles {
pp , _ := filepath . Glob ( ff )
profiles = append ( profiles , pp ... )
}
2018-06-28 13:29:20 +08:00
for _ , profile := range profiles {
2018-07-05 00:59:33 +08:00
if stat , err := os . Stat ( profile ) ; err != nil || ! stat . IsDir ( ) {
continue
}
2019-06-01 21:55:58 +08:00
if pathExists ( filepath . Join ( profile , "cert9.db" ) ) {
2018-06-28 13:29:20 +08:00
f ( "sql:" + profile )
found ++
2019-06-01 21:55:58 +08:00
} else if pathExists ( filepath . Join ( profile , "cert8.db" ) ) {
2018-07-13 01:47:05 +08:00
f ( "dbm:" + profile )
found ++
2018-06-28 13:29:20 +08:00
}
}
return
}