- Timestamp:
- Jun 2, 2020, 9:24:22 AM (5 years ago)
- Location:
- trunk
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/db.go
r284 r307 21 21 Username string 22 22 Password string 23 } 24 25 // TLS client certificate authentication. 26 External struct { 27 // X.509 certificate in DER form. 28 CertBlob []byte 29 // PKCS#8 private key in DER form. 30 PrivKeyBlob []byte 23 31 } 24 32 } … … 69 77 sasl_plain_username VARCHAR(255), 70 78 sasl_plain_password VARCHAR(255), 79 sasl_external_cert BLOB DEFAULT NULL, 80 sasl_external_key BLOB DEFAULT NULL, 71 81 FOREIGN KEY(user) REFERENCES User(username), 72 82 UNIQUE(user, addr, nick) … … 88 98 "ALTER TABLE Network ADD COLUMN connect_commands VARCHAR(1023)", 89 99 "ALTER TABLE Channel ADD COLUMN detached INTEGER NOT NULL DEFAULT 0", 100 "ALTER TABLE Network ADD COLUMN sasl_external_cert BLOB DEFAULT NULL", 101 "ALTER TABLE Network ADD COLUMN sasl_external_key BLOB DEFAULT NULL", 90 102 } 91 103 … … 239 251 240 252 rows, err := db.db.Query(`SELECT id, name, addr, nick, username, realname, pass, 241 connect_commands, sasl_mechanism, sasl_plain_username, sasl_plain_password 253 connect_commands, sasl_mechanism, sasl_plain_username, sasl_plain_password, 254 sasl_external_cert, sasl_external_key 242 255 FROM Network 243 256 WHERE user = ?`, … … 254 267 var saslMechanism, saslPlainUsername, saslPlainPassword *string 255 268 err := rows.Scan(&net.ID, &name, &net.Addr, &net.Nick, &username, &realname, 256 &pass, &connectCommands, &saslMechanism, &saslPlainUsername, &saslPlainPassword) 269 &pass, &connectCommands, &saslMechanism, &saslPlainUsername, &saslPlainPassword, 270 &net.SASL.External.CertBlob, &net.SASL.External.PrivKeyBlob) 257 271 if err != nil { 258 272 return nil, err … … 294 308 saslPlainUsername = toStringPtr(network.SASL.Plain.Username) 295 309 saslPlainPassword = toStringPtr(network.SASL.Plain.Password) 310 network.SASL.External.CertBlob = nil 311 network.SASL.External.PrivKeyBlob = nil 312 case "EXTERNAL": 313 // keep saslPlain* nil 296 314 default: 297 315 return fmt.Errorf("soju: cannot store network: unsupported SASL mechanism %q", network.SASL.Mechanism) … … 303 321 _, err = db.db.Exec(`UPDATE Network 304 322 SET name = ?, addr = ?, nick = ?, username = ?, realname = ?, pass = ?, connect_commands = ?, 305 sasl_mechanism = ?, sasl_plain_username = ?, sasl_plain_password = ? 323 sasl_mechanism = ?, sasl_plain_username = ?, sasl_plain_password = ?, 324 sasl_external_cert = ?, sasl_external_key = ? 306 325 WHERE id = ?`, 307 326 netName, network.Addr, network.Nick, netUsername, realname, pass, connectCommands, 308 saslMechanism, saslPlainUsername, saslPlainPassword, network.ID) 327 saslMechanism, saslPlainUsername, saslPlainPassword, 328 network.SASL.External.CertBlob, network.SASL.External.PrivKeyBlob, 329 network.ID) 309 330 } else { 310 331 var res sql.Result 311 332 res, err = db.db.Exec(`INSERT INTO Network(user, name, addr, nick, username, 312 333 realname, pass, connect_commands, sasl_mechanism, sasl_plain_username, 313 sasl_plain_password )314 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )`,334 sasl_plain_password, sasl_external_cert, sasl_external_key) 335 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, 315 336 username, netName, network.Addr, network.Nick, netUsername, realname, pass, connectCommands, 316 saslMechanism, saslPlainUsername, saslPlainPassword) 337 saslMechanism, saslPlainUsername, saslPlainPassword, network.SASL.External.CertBlob, 338 network.SASL.External.PrivKeyBlob) 317 339 if err != nil { 318 340 return err -
trunk/doc/soju.1.scd
r284 r307 120 120 is used. 121 121 122 *certfp generate* *[options...]* <network name> 123 Generate self-signed certificate and use it for authentication. 124 125 Generates RSA-3072 private key by default. 126 127 Options are: 128 129 *-key-type* <type> 130 Private key algoritm to use. Valid values are: rsa, ecdsa, ed25519. 131 ecdsa uses NIST P-521 curve. 132 133 *-bits* <bits> 134 Size of RSA key to generate. Ignored for other key types. 135 136 *certfp fingerprint* <network name> 137 Show SHA-1 and SHA-256 fingerprints for the certificate 138 currently used with the network. 139 140 *certfp reset* <network name> 141 Disable SASL EXTERNAL authentication and remove stored certificate. 142 122 143 *network delete* <name> 123 144 Disconnect and delete a network. -
trunk/downstream.go
r306 r307 1524 1524 } 1525 1525 1526 // User may have e.g. EXTERNAL mechanism configured. We do not want to 1527 // automatically erase the key pair or any other credentials. 1528 if uc.network.SASL.Mechanism != "" && uc.network.SASL.Mechanism != "PLAIN" { 1529 return 1530 } 1531 1526 1532 dc.logger.Printf("auto-saving NickServ credentials with username %q", username) 1527 1533 n := uc.network -
trunk/service.go
r279 r307 2 2 3 3 import ( 4 "crypto" 5 "crypto/ecdsa" 6 "crypto/ed25519" 7 "crypto/elliptic" 8 "crypto/rand" 9 "crypto/rsa" 10 "crypto/sha1" 11 "crypto/sha256" 12 "crypto/x509" 13 "crypto/x509/pkix" 14 "encoding/hex" 15 "errors" 4 16 "flag" 5 17 "fmt" 6 18 "io/ioutil" 19 "math/big" 7 20 "strings" 21 "time" 8 22 9 23 "github.com/google/shlex" … … 120 134 }, 121 135 }, 136 "certfp": { 137 children: serviceCommandSet{ 138 "generate": { 139 usage: "[-key-type rsa|ecdsa|ed25519] [-bits N] <network name>", 140 desc: "generate a new self-signed certificate, defaults to using RSA-3072 key", 141 handle: handleServiceCertfpGenerate, 142 }, 143 "fingerprint": { 144 usage: "<network name>", 145 desc: "show fingerprints of certificate associated with the network", 146 handle: handleServiceCertfpFingerprints, 147 }, 148 "reset": { 149 usage: "<network name>", 150 desc: "disable SASL EXTERNAL authentication and remove stored certificate", 151 handle: handleServiceCertfpReset, 152 }, 153 }, 154 }, 122 155 "change-password": { 123 156 usage: "<new password>", … … 126 159 }, 127 160 } 161 } 162 163 func handleServiceCertfpGenerate(dc *downstreamConn, params []string) error { 164 fs := newFlagSet() 165 keyType := fs.String("key-type", "rsa", "key type to generate (rsa, ecdsa, ed25519)") 166 bits := fs.Int("bits", 3072, "size of key to generate, meaningful only for RSA") 167 168 if err := fs.Parse(params); err != nil { 169 return err 170 } 171 172 if len(fs.Args()) != 1 { 173 return errors.New("exactly one argument is required") 174 } 175 176 net := dc.user.getNetwork(fs.Arg(0)) 177 if net == nil { 178 return fmt.Errorf("unknown network %q", fs.Arg(0)) 179 } 180 181 var ( 182 privKey crypto.PrivateKey 183 pubKey crypto.PublicKey 184 ) 185 switch *keyType { 186 case "rsa": 187 key, err := rsa.GenerateKey(rand.Reader, *bits) 188 if err != nil { 189 return err 190 } 191 privKey = key 192 pubKey = key.Public() 193 case "ecdsa": 194 key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) 195 if err != nil { 196 return err 197 } 198 privKey = key 199 pubKey = key.Public() 200 case "ed25519": 201 var err error 202 pubKey, privKey, err = ed25519.GenerateKey(rand.Reader) 203 if err != nil { 204 return err 205 } 206 } 207 208 // Using PKCS#8 allows easier extension for new key types. 209 privKeyBytes, err := x509.MarshalPKCS8PrivateKey(privKey) 210 if err != nil { 211 return err 212 } 213 214 notBefore := time.Now() 215 // Lets make a fair assumption nobody will use the same cert for more than 20 years... 216 notAfter := notBefore.Add(24 * time.Hour * 365 * 20) 217 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 218 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 219 if err != nil { 220 return err 221 } 222 cert := &x509.Certificate{ 223 SerialNumber: serialNumber, 224 Subject: pkix.Name{CommonName: "soju auto-generated certificate"}, 225 NotBefore: notBefore, 226 NotAfter: notAfter, 227 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 228 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, 229 } 230 derBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, pubKey, privKey) 231 if err != nil { 232 return err 233 } 234 235 net.SASL.External.CertBlob = derBytes 236 net.SASL.External.PrivKeyBlob = privKeyBytes 237 net.SASL.Mechanism = "EXTERNAL" 238 239 if err := dc.srv.db.StoreNetwork(net.Username, &net.Network); err != nil { 240 return err 241 } 242 243 sendServicePRIVMSG(dc, "certificate generated") 244 245 sha1Sum := sha1.Sum(derBytes) 246 sendServicePRIVMSG(dc, "SHA-1 fingerprint: "+hex.EncodeToString(sha1Sum[:])) 247 sha256Sum := sha256.Sum256(derBytes) 248 sendServicePRIVMSG(dc, "SHA-256 fingerprint: "+hex.EncodeToString(sha256Sum[:])) 249 250 return nil 251 } 252 253 func handleServiceCertfpFingerprints(dc *downstreamConn, params []string) error { 254 if len(params) != 1 { 255 return fmt.Errorf("expected exactly one argument") 256 } 257 258 net := dc.user.getNetwork(params[0]) 259 if net == nil { 260 return fmt.Errorf("unknown network %q", params[0]) 261 } 262 263 sha1Sum := sha1.Sum(net.SASL.External.CertBlob) 264 sendServicePRIVMSG(dc, "SHA-1 fingerprint: "+hex.EncodeToString(sha1Sum[:])) 265 sha256Sum := sha256.Sum256(net.SASL.External.CertBlob) 266 sendServicePRIVMSG(dc, "SHA-256 fingerprint: "+hex.EncodeToString(sha256Sum[:])) 267 return nil 268 } 269 270 func handleServiceCertfpReset(dc *downstreamConn, params []string) error { 271 if len(params) != 1 { 272 return fmt.Errorf("expected exactly one argument") 273 } 274 275 net := dc.user.getNetwork(params[0]) 276 if net == nil { 277 return fmt.Errorf("unknown network %q", params[0]) 278 } 279 280 net.SASL.External.CertBlob = nil 281 net.SASL.External.PrivKeyBlob = nil 282 283 if net.SASL.Mechanism == "EXTERNAL" { 284 net.SASL.Mechanism = "" 285 } 286 if err := dc.srv.db.StoreNetwork(dc.user.Username, &net.Network); err != nil { 287 return err 288 } 289 290 sendServicePRIVMSG(dc, "certificate reset") 291 return nil 128 292 } 129 293 -
trunk/upstream.go
r305 r307 2 2 3 3 import ( 4 "crypto" 5 "crypto/sha256" 4 6 "crypto/tls" 7 "crypto/x509" 5 8 "encoding/base64" 6 9 "errors" … … 101 104 102 105 logger.Printf("connecting to TLS server at address %q", addr) 103 netConn, err = tls.DialWithDialer(&dialer, "tcp", addr, nil) 106 107 var cfg *tls.Config 108 if network.SASL.Mechanism == "EXTERNAL" { 109 if network.SASL.External.CertBlob == nil { 110 return nil, fmt.Errorf("missing certificate for authentication") 111 } 112 if network.SASL.External.PrivKeyBlob == nil { 113 return nil, fmt.Errorf("missing private key for authentication") 114 } 115 key, err := x509.ParsePKCS8PrivateKey(network.SASL.External.PrivKeyBlob) 116 if err != nil { 117 return nil, fmt.Errorf("failed to parse private key: %v", err) 118 } 119 cfg = &tls.Config{ 120 Certificates: []tls.Certificate{ 121 { 122 Certificate: [][]byte{network.SASL.External.CertBlob}, 123 PrivateKey: key.(crypto.PrivateKey), 124 }, 125 }, 126 } 127 logger.Printf("using TLS client certificate %x", sha256.Sum256(network.SASL.External.CertBlob)) 128 } 129 130 netConn, err = tls.DialWithDialer(&dialer, "tcp", addr, cfg) 104 131 case "irc+insecure": 105 132 if !strings.ContainsRune(addr, ':') { … … 1400 1427 uc.logger.Printf("starting SASL PLAIN authentication with username %q", auth.Plain.Username) 1401 1428 uc.saslClient = sasl.NewPlainClient("", auth.Plain.Username, auth.Plain.Password) 1429 case "EXTERNAL": 1430 uc.logger.Printf("starting SASL EXTERNAL authentication") 1431 uc.saslClient = sasl.NewExternalClient("") 1402 1432 default: 1403 1433 return fmt.Errorf("unsupported SASL mechanism %q", name)
Note:
See TracChangeset
for help on using the changeset viewer.