00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "includes.h"
00028 #include <keyutils.h>
00029
00030 #include "cifs_spnego.h"
00031
00032 const char *CIFSSPNEGO_VERSION = "1.1";
00033 static const char *prog = "cifs.spnego";
00034 typedef enum _secType {
00035 KRB5,
00036 MS_KRB5
00037 } secType_t;
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059 int handle_krb5_mech(const char *oid, const char *principal,
00060 DATA_BLOB * secblob, DATA_BLOB * sess_key)
00061 {
00062 int retval;
00063 DATA_BLOB tkt, tkt_wrapped;
00064
00065
00066 retval = cli_krb5_get_ticket(principal, 0,
00067 &tkt, sess_key, 0, NULL, NULL);
00068
00069 if (retval)
00070 return retval;
00071
00072
00073 tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
00074
00075
00076 *secblob = gen_negTokenInit(OID_KERBEROS5, tkt_wrapped);
00077
00078 data_blob_free(&tkt_wrapped);
00079 data_blob_free(&tkt);
00080 return retval;
00081 }
00082
00083 #define DKD_HAVE_HOSTNAME 1
00084 #define DKD_HAVE_VERSION 2
00085 #define DKD_HAVE_SEC 4
00086 #define DKD_HAVE_IPV4 8
00087 #define DKD_HAVE_IPV6 16
00088 #define DKD_HAVE_UID 32
00089 #define DKD_MUSTHAVE_SET (DKD_HAVE_HOSTNAME|DKD_HAVE_VERSION|DKD_HAVE_SEC)
00090
00091 int decode_key_description(const char *desc, int *ver, secType_t * sec,
00092 char **hostname, uid_t * uid)
00093 {
00094 int retval = 0;
00095 char *pos;
00096 const char *tkn = desc;
00097
00098 do {
00099 pos = index(tkn, ';');
00100 if (strncmp(tkn, "host=", 5) == 0) {
00101 int len;
00102
00103 if (pos == NULL) {
00104 len = strlen(tkn);
00105 } else {
00106 len = pos - tkn;
00107 }
00108 len -= 4;
00109 SAFE_FREE(*hostname);
00110 *hostname = SMB_XMALLOC_ARRAY(char, len);
00111 strlcpy(*hostname, tkn + 5, len);
00112 retval |= DKD_HAVE_HOSTNAME;
00113 } else if (strncmp(tkn, "ipv4=", 5) == 0) {
00114
00115 } else if (strncmp(tkn, "ipv6=", 5) == 0) {
00116
00117 } else if (strncmp(tkn, "sec=", 4) == 0) {
00118 if (strncmp(tkn + 4, "krb5", 4) == 0) {
00119 retval |= DKD_HAVE_SEC;
00120 *sec = KRB5;
00121 }
00122 } else if (strncmp(tkn, "uid=", 4) == 0) {
00123 errno = 0;
00124 *uid = strtol(tkn + 4, NULL, 16);
00125 if (errno != 0) {
00126 syslog(LOG_WARNING, "Invalid uid format: %s",
00127 strerror(errno));
00128 return 1;
00129 } else {
00130 retval |= DKD_HAVE_UID;
00131 }
00132 } else if (strncmp(tkn, "ver=", 4) == 0) {
00133 errno = 0;
00134 *ver = strtol(tkn + 4, NULL, 16);
00135 if (errno != 0) {
00136 syslog(LOG_WARNING,
00137 "Invalid version format: %s",
00138 strerror(errno));
00139 return 1;
00140 } else {
00141 retval |= DKD_HAVE_VERSION;
00142 }
00143 }
00144 if (pos == NULL)
00145 break;
00146 tkn = pos + 1;
00147 } while (tkn);
00148 return retval;
00149 }
00150
00151 int cifs_resolver(const key_serial_t key, const char *key_descr)
00152 {
00153 int c;
00154 struct addrinfo *addr;
00155 char ip[INET6_ADDRSTRLEN];
00156 void *p;
00157 const char *keyend = key_descr;
00158
00159 for (c = 1; c <= 4; c++) {
00160 keyend = index(keyend+1, ';');
00161 if (!keyend) {
00162 syslog(LOG_WARNING, "invalid key description: %s",
00163 key_descr);
00164 return 1;
00165 }
00166 }
00167 keyend++;
00168
00169
00170 c = getaddrinfo(keyend, NULL, NULL, &addr);
00171 if (c) {
00172 syslog(LOG_WARNING, "unable to resolve hostname: %s [%s]",
00173 keyend, gai_strerror(c));
00174 return 1;
00175 }
00176
00177
00178 if (addr->ai_family == AF_INET) {
00179 p = &(((struct sockaddr_in *)addr->ai_addr)->sin_addr);
00180 } else {
00181 p = &(((struct sockaddr_in6 *)addr->ai_addr)->sin6_addr);
00182 }
00183 if (!inet_ntop(addr->ai_family, p, ip, sizeof(ip))) {
00184 syslog(LOG_WARNING, "%s: inet_ntop: %s",
00185 __FUNCTION__, strerror(errno));
00186 freeaddrinfo(addr);
00187 return 1;
00188 }
00189
00190
00191 c = keyctl_instantiate(key, ip, strlen(ip)+1, 0);
00192 if (c == -1) {
00193 syslog(LOG_WARNING, "%s: keyctl_instantiate: %s",
00194 __FUNCTION__, strerror(errno));
00195 freeaddrinfo(addr);
00196 return 1;
00197 }
00198
00199 freeaddrinfo(addr);
00200 return 0;
00201 }
00202
00203 int main(const int argc, char *const argv[])
00204 {
00205 struct cifs_spnego_msg *keydata = NULL;
00206 DATA_BLOB secblob = data_blob_null;
00207 DATA_BLOB sess_key = data_blob_null;
00208 secType_t sectype;
00209 key_serial_t key;
00210 size_t datalen;
00211 long rc = 1;
00212 uid_t uid;
00213 int kernel_upcall_version;
00214 int c, use_cifs_service_prefix = 0;
00215 char *buf, *hostname = NULL;
00216
00217 openlog(prog, 0, LOG_DAEMON);
00218 if (argc < 1) {
00219 syslog(LOG_WARNING, "Usage: %s [-c] key_serial", prog);
00220 goto out;
00221 }
00222
00223 while ((c = getopt(argc, argv, "cv")) != -1) {
00224 switch (c) {
00225 case 'c':{
00226 use_cifs_service_prefix = 1;
00227 break;
00228 }
00229 case 'v':{
00230 syslog(LOG_WARNING, "version: %s", CIFSSPNEGO_VERSION);
00231 fprintf(stderr, "version: %s", CIFSSPNEGO_VERSION);
00232 break;
00233 }
00234 default:{
00235 syslog(LOG_WARNING, "unknow option: %c", c);
00236 goto out;
00237 }
00238 }
00239 }
00240
00241 errno = 0;
00242 key = strtol(argv[optind], NULL, 10);
00243 if (errno != 0) {
00244 syslog(LOG_WARNING, "Invalid key format: %s", strerror(errno));
00245 goto out;
00246 }
00247
00248 rc = keyctl_describe_alloc(key, &buf);
00249 if (rc == -1) {
00250 syslog(LOG_WARNING, "keyctl_describe_alloc failed: %s",
00251 strerror(errno));
00252 rc = 1;
00253 goto out;
00254 }
00255
00256 if (strncmp(buf, "cifs.resolver", sizeof("cifs.resolver")-1) == 0) {
00257 rc = cifs_resolver(key, buf);
00258 goto out;
00259 }
00260
00261 rc = decode_key_description(buf, &kernel_upcall_version, §ype,
00262 &hostname, &uid);
00263 if ((rc & DKD_MUSTHAVE_SET) != DKD_MUSTHAVE_SET) {
00264 syslog(LOG_WARNING,
00265 "unable to get from description necessary params");
00266 rc = 1;
00267 SAFE_FREE(buf);
00268 goto out;
00269 }
00270 SAFE_FREE(buf);
00271
00272 if (kernel_upcall_version != CIFS_SPNEGO_UPCALL_VERSION) {
00273 syslog(LOG_WARNING,
00274 "incompatible kernel upcall version: 0x%x",
00275 kernel_upcall_version);
00276 rc = 1;
00277 goto out;
00278 }
00279
00280 if (rc & DKD_HAVE_UID) {
00281 rc = setuid(uid);
00282 if (rc == -1) {
00283 syslog(LOG_WARNING, "setuid: %s", strerror(errno));
00284 goto out;
00285 }
00286 }
00287
00288
00289
00290
00291
00292 switch (sectype) {
00293 case KRB5:{
00294 char *princ;
00295 size_t len;
00296
00297
00298 len = strlen(hostname) + 5 + 1;
00299 princ = SMB_XMALLOC_ARRAY(char, len);
00300 if (!princ) {
00301 rc = 1;
00302 break;
00303 }
00304 if (use_cifs_service_prefix) {
00305 strlcpy(princ, "cifs/", len);
00306 } else {
00307 strlcpy(princ, "host/", len);
00308 }
00309 strlcpy(princ + 5, hostname, len - 5);
00310
00311 rc = handle_krb5_mech(OID_KERBEROS5, princ,
00312 &secblob, &sess_key);
00313 SAFE_FREE(princ);
00314 break;
00315 }
00316 default:{
00317 syslog(LOG_WARNING, "sectype: %d is not implemented",
00318 sectype);
00319 rc = 1;
00320 break;
00321 }
00322 }
00323
00324 if (rc) {
00325 goto out;
00326 }
00327
00328
00329 datalen =
00330 sizeof(struct cifs_spnego_msg) + secblob.length + sess_key.length;
00331 keydata = (struct cifs_spnego_msg*)SMB_XMALLOC_ARRAY(char, datalen);
00332 if (!keydata) {
00333 rc = 1;
00334 goto out;
00335 }
00336 keydata->version = CIFS_SPNEGO_UPCALL_VERSION;
00337 keydata->flags = 0;
00338 keydata->sesskey_len = sess_key.length;
00339 keydata->secblob_len = secblob.length;
00340 memcpy(&(keydata->data), sess_key.data, sess_key.length);
00341 memcpy(&(keydata->data) + keydata->sesskey_len,
00342 secblob.data, secblob.length);
00343
00344
00345 rc = keyctl_instantiate(key, keydata, datalen, 0);
00346 if (rc == -1) {
00347 syslog(LOG_WARNING, "keyctl_instantiate: %s", strerror(errno));
00348 goto out;
00349 }
00350
00351
00352
00353
00354 out:
00355 data_blob_free(&secblob);
00356 data_blob_free(&sess_key);
00357 SAFE_FREE(hostname);
00358 SAFE_FREE(keydata);
00359 return rc;
00360 }