1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package remotecmd
18
19 import (
20 "errors"
21 "fmt"
22 "net/url"
23 "os"
24 "path/filepath"
25
26 "github.com/sassoftware/relic/cmdline/shared"
27 "github.com/sassoftware/relic/signers"
28 "github.com/spf13/cobra"
29 )
30
31 var SignCmd = &cobra.Command{
32 Use: "sign",
33 Short: "Sign a package using a remote signing server",
34 RunE: signCmd,
35 }
36
37 var (
38 argIfUnsigned bool
39 argSigType string
40 )
41
42 func init() {
43 RemoteCmd.AddCommand(SignCmd)
44 SignCmd.Flags().StringVarP(&argKeyName, "key", "k", "", "Name of key on remote server to use")
45 SignCmd.Flags().StringVarP(&argFile, "file", "f", "", "Input file to sign")
46 SignCmd.Flags().StringVarP(&argOutput, "output", "o", "", "Output file. Defaults to same as --file.")
47 SignCmd.Flags().StringVarP(&argSigType, "sig-type", "T", "", "Specify signature type (default: auto-detect)")
48 SignCmd.Flags().BoolVar(&argIfUnsigned, "if-unsigned", false, "Skip signing if the file already has a signature")
49 shared.AddDigestFlag(SignCmd)
50 shared.AddLateHook(func() {
51 signers.MergeFlags(SignCmd.Flags())
52 })
53 }
54
55 func signCmd(cmd *cobra.Command, args []string) (err error) {
56 if argFile == "" || argKeyName == "" {
57 return errors.New("--file and --key are required")
58 }
59 if argOutput == "" {
60 argOutput = argFile
61 }
62
63 mod, err := signers.ByFile(argFile, argSigType)
64 if err != nil {
65 return shared.Fail(err)
66 }
67 if mod.Sign == nil {
68 return shared.Fail(errors.New("can't sign this type of file"))
69 }
70
71 flags, err := mod.FlagsFromCmdline(cmd.Flags())
72 if err != nil {
73 return shared.Fail(err)
74 }
75 var infile *os.File
76 if argFile == "-" {
77 if argIfUnsigned {
78 return shared.Fail(errors.New("cannot use --if-unsigned with standard input"))
79 }
80 if !mod.AllowStdin {
81 return shared.Fail(errors.New("this signature type does not support reading from stdin"))
82 }
83 infile = os.Stdin
84 } else {
85
86 if argOutput == argFile {
87 infile, err = os.OpenFile(argFile, os.O_RDWR, 0)
88 } else {
89 infile, err = os.Open(argFile)
90 }
91 if err != nil {
92 return shared.Fail(err)
93 }
94 defer infile.Close()
95 }
96 if argIfUnsigned {
97 if signed, err := mod.IsSigned(infile); err != nil {
98 return shared.Fail(err)
99 } else if signed {
100 fmt.Fprintf(os.Stderr, "skipping already-signed file: %s\n", argFile)
101 return nil
102 }
103 if _, err := infile.Seek(0, 0); err != nil {
104 return shared.Fail(fmt.Errorf("failed to rewind input file: %s", err))
105 }
106 }
107
108 hash, err := shared.GetDigest()
109 if err != nil {
110 return err
111 }
112 opts := signers.SignOpts{
113 Path: argFile,
114 Hash: hash,
115 Flags: flags,
116 }
117 transform, err := mod.GetTransform(infile, opts)
118 if err != nil {
119 return shared.Fail(err)
120 }
121
122 values := url.Values{}
123 values.Add("key", argKeyName)
124 values.Add("filename", filepath.Base(argFile))
125 values.Add("sigtype", mod.Name)
126 if err := flags.ToQuery(values); err != nil {
127 return shared.Fail(err)
128 }
129 if err := setDigestQueryParam(values); err != nil {
130 return err
131 }
132
133 response, err := CallRemote("sign", "POST", &values, transform)
134 if err != nil {
135 return shared.Fail(err)
136 }
137 defer response.Body.Close()
138
139 if err := transform.Apply(argOutput, response.Header.Get("Content-Type"), response.Body); err != nil {
140 return shared.Fail(err)
141 }
142
143 if mod.Fixup != nil {
144 f, err := os.OpenFile(argOutput, os.O_RDWR, 0)
145 if err != nil {
146 return shared.Fail(err)
147 }
148 defer f.Close()
149 if err := mod.Fixup(f); err != nil {
150 return shared.Fail(err)
151 }
152 }
153
154 fmt.Fprintf(os.Stderr, "Signed %s\n", argFile)
155 return nil
156 }
157
View as plain text