| 
									
										
										
										
											2019-10-10 14:37:27 -06:00
										 |  |  | // Copyright 2015 Matthew Holt and The Caddy Authors | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License. | 
					
						
							|  |  |  | // You may obtain a copy of the License at | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //     http://www.apache.org/licenses/LICENSE-2.0 | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and | 
					
						
							|  |  |  | // limitations under the License. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package caddyauth | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2020-05-11 15:10:47 -05:00
										 |  |  | 	"bufio" | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2019-10-10 14:37:27 -06:00
										 |  |  | 	"encoding/base64" | 
					
						
							|  |  |  | 	"flag" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2020-05-11 15:10:47 -05:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2020-05-21 15:09:49 -04:00
										 |  |  | 	"os/signal" | 
					
						
							| 
									
										
										
										
											2019-10-10 14:37:27 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/caddyserver/caddy/v2" | 
					
						
							|  |  |  | 	caddycmd "github.com/caddyserver/caddy/v2/cmd" | 
					
						
							|  |  |  | 	"golang.org/x/crypto/bcrypt" | 
					
						
							|  |  |  | 	"golang.org/x/crypto/scrypt" | 
					
						
							| 
									
										
										
										
											2020-05-11 15:10:47 -05:00
										 |  |  | 	"golang.org/x/crypto/ssh/terminal" | 
					
						
							| 
									
										
										
										
											2019-10-10 14:37:27 -06:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func init() { | 
					
						
							|  |  |  | 	caddycmd.RegisterCommand(caddycmd.Command{ | 
					
						
							|  |  |  | 		Name:  "hash-password", | 
					
						
							|  |  |  | 		Func:  cmdHashPassword, | 
					
						
							| 
									
										
										
										
											2020-05-11 15:10:47 -05:00
										 |  |  | 		Usage: "[--algorithm <name>] [--salt <string>] [--plaintext <password>]", | 
					
						
							| 
									
										
										
										
											2019-10-10 14:37:27 -06:00
										 |  |  | 		Short: "Hashes a password and writes base64", | 
					
						
							|  |  |  | 		Long: ` | 
					
						
							|  |  |  | Convenient way to hash a plaintext password. The resulting | 
					
						
							|  |  |  | hash is written to stdout as a base64 string. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-11 15:10:47 -05:00
										 |  |  | --plaintext, when omitted, will be read from stdin. If | 
					
						
							|  |  |  | Caddy is attached to a controlling tty, the plaintext will | 
					
						
							|  |  |  | not be echoed. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-10 14:37:27 -06:00
										 |  |  | --algorithm may be bcrypt or scrypt. If script, the default | 
					
						
							|  |  |  | parameters are used. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Use the --salt flag for algorithms which require a salt to | 
					
						
							|  |  |  | be provided (scrypt). | 
					
						
							|  |  |  | `, | 
					
						
							|  |  |  | 		Flags: func() *flag.FlagSet { | 
					
						
							|  |  |  | 			fs := flag.NewFlagSet("hash-password", flag.ExitOnError) | 
					
						
							|  |  |  | 			fs.String("algorithm", "bcrypt", "Name of the hash algorithm") | 
					
						
							|  |  |  | 			fs.String("plaintext", "", "The plaintext password") | 
					
						
							|  |  |  | 			fs.String("salt", "", "The password salt") | 
					
						
							|  |  |  | 			return fs | 
					
						
							|  |  |  | 		}(), | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func cmdHashPassword(fs caddycmd.Flags) (int, error) { | 
					
						
							| 
									
										
										
										
											2020-05-11 15:10:47 -05:00
										 |  |  | 	var err error | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-10 14:37:27 -06:00
										 |  |  | 	algorithm := fs.String("algorithm") | 
					
						
							|  |  |  | 	plaintext := []byte(fs.String("plaintext")) | 
					
						
							|  |  |  | 	salt := []byte(fs.String("salt")) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-28 15:08:45 -06:00
										 |  |  | 	if len(plaintext) == 0 { | 
					
						
							| 
									
										
										
										
											2020-05-21 15:09:49 -04:00
										 |  |  | 		fd := int(os.Stdin.Fd()) | 
					
						
							|  |  |  | 		if terminal.IsTerminal(fd) { | 
					
						
							|  |  |  | 			// ensure the terminal state is restored on SIGINT | 
					
						
							|  |  |  | 			state, _ := terminal.GetState(fd) | 
					
						
							|  |  |  | 			c := make(chan os.Signal) | 
					
						
							|  |  |  | 			signal.Notify(c, os.Interrupt) | 
					
						
							|  |  |  | 			go func() { | 
					
						
							|  |  |  | 				<-c | 
					
						
							|  |  |  | 				_ = terminal.Restore(fd, state) | 
					
						
							|  |  |  | 				os.Exit(caddy.ExitCodeFailedStartup) | 
					
						
							|  |  |  | 			}() | 
					
						
							|  |  |  | 			defer signal.Stop(c) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			fmt.Fprint(os.Stderr, "Enter password: ") | 
					
						
							|  |  |  | 			plaintext, err = terminal.ReadPassword(fd) | 
					
						
							|  |  |  | 			fmt.Fprintln(os.Stderr) | 
					
						
							| 
									
										
										
										
											2020-05-11 15:10:47 -05:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return caddy.ExitCodeFailedStartup, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-21 15:09:49 -04:00
										 |  |  | 			fmt.Fprint(os.Stderr, "Confirm password: ") | 
					
						
							|  |  |  | 			confirmation, err := terminal.ReadPassword(fd) | 
					
						
							|  |  |  | 			fmt.Fprintln(os.Stderr) | 
					
						
							| 
									
										
										
										
											2020-05-11 15:10:47 -05:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return caddy.ExitCodeFailedStartup, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if !bytes.Equal(plaintext, confirmation) { | 
					
						
							|  |  |  | 				return caddy.ExitCodeFailedStartup, fmt.Errorf("password does not match") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			rd := bufio.NewReader(os.Stdin) | 
					
						
							|  |  |  | 			plaintext, err = rd.ReadBytes('\n') | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return caddy.ExitCodeFailedStartup, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			plaintext = plaintext[:len(plaintext)-1] // Trailing newline | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if len(plaintext) == 0 { | 
					
						
							|  |  |  | 			return caddy.ExitCodeFailedStartup, fmt.Errorf("plaintext is required") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-10-28 15:08:45 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-10 14:37:27 -06:00
										 |  |  | 	var hash []byte | 
					
						
							|  |  |  | 	switch algorithm { | 
					
						
							|  |  |  | 	case "bcrypt": | 
					
						
							| 
									
										
										
										
											2020-07-17 12:20:41 -06:00
										 |  |  | 		hash, err = bcrypt.GenerateFromPassword(plaintext, 14) | 
					
						
							| 
									
										
										
										
											2019-10-10 14:37:27 -06:00
										 |  |  | 	case "scrypt": | 
					
						
							|  |  |  | 		def := ScryptHash{} | 
					
						
							|  |  |  | 		def.SetDefaults() | 
					
						
							|  |  |  | 		hash, err = scrypt.Key(plaintext, salt, def.N, def.R, def.P, def.KeyLength) | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return caddy.ExitCodeFailedStartup, fmt.Errorf("unrecognized hash algorithm: %s", algorithm) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return caddy.ExitCodeFailedStartup, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-09 00:31:51 +03:00
										 |  |  | 	hashBase64 := base64.StdEncoding.EncodeToString(hash) | 
					
						
							| 
									
										
										
										
											2019-10-10 14:37:27 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	fmt.Println(hashBase64) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0, nil | 
					
						
							|  |  |  | } |