aboutsummaryrefslogtreecommitdiffstats
path: root/security/smack/smack_lsm.c
diff options
context:
space:
mode:
authorCasey Schaufler <casey@schaufler-ca.com>2011-01-17 11:05:27 -0500
committerCasey Schaufler <casey@schaufler-ca.com>2011-01-17 11:05:27 -0500
commit7898e1f8e9eb1bee88c92d636e0ab93f2cbe31c6 (patch)
treed4aaa367bb42d0ff9d1e4ba227f248b5b9cd7687 /security/smack/smack_lsm.c
parentaeda4ac3efc29e4d55989abd0a73530453aa69ba (diff)
Subject: [PATCH] Smack: mmap controls for library containment
In the embedded world there are often situations where libraries are updated from a variety of sources, for a variety of reasons, and with any number of security characteristics. These differences might include privilege required for a given library provided interface to function properly, as occurs from time to time in graphics libraries. There are also cases where it is important to limit use of libraries based on the provider of the library and the security aware application may make choices based on that criteria. These issues are addressed by providing an additional Smack label that may optionally be assigned to an object, the SMACK64MMAP attribute. An mmap operation is allowed if there is no such attribute. If there is a SMACK64MMAP attribute the mmap is permitted only if a subject with that label has all of the access permitted a subject with the current task label. Security aware applications may from time to time wish to reduce their "privilege" to avoid accidental use of privilege. One case where this arises is the environment in which multiple sources provide libraries to perform the same functions. An application may know that it should eschew services made available from a particular vendor, or of a particular version. In support of this a secondary list of Smack rules has been added that is local to the task. This list is consulted only in the case where the global list has approved access. It can only further restrict access. Unlike the global last, if no entry is found on the local list access is granted. An application can add entries to its own list by writing to /smack/load-self. The changes appear large as they involve refactoring the list handling to accomodate there being more than one rule list. Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
Diffstat (limited to 'security/smack/smack_lsm.c')
-rw-r--r--security/smack/smack_lsm.c269
1 files changed, 227 insertions, 42 deletions
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 533bf3255d7f..123a499ded37 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -84,6 +84,56 @@ struct inode_smack *new_inode_smack(char *smack)
84 return isp; 84 return isp;
85} 85}
86 86
87/**
88 * new_task_smack - allocate a task security blob
89 * @smack: a pointer to the Smack label to use in the blob
90 *
91 * Returns the new blob or NULL if there's no memory available
92 */
93static struct task_smack *new_task_smack(char *task, char *forked, gfp_t gfp)
94{
95 struct task_smack *tsp;
96
97 tsp = kzalloc(sizeof(struct task_smack), gfp);
98 if (tsp == NULL)
99 return NULL;
100
101 tsp->smk_task = task;
102 tsp->smk_forked = forked;
103 INIT_LIST_HEAD(&tsp->smk_rules);
104 mutex_init(&tsp->smk_rules_lock);
105
106 return tsp;
107}
108
109/**
110 * smk_copy_rules - copy a rule set
111 * @nhead - new rules header pointer
112 * @ohead - old rules header pointer
113 *
114 * Returns 0 on success, -ENOMEM on error
115 */
116static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead,
117 gfp_t gfp)
118{
119 struct smack_rule *nrp;
120 struct smack_rule *orp;
121 int rc = 0;
122
123 INIT_LIST_HEAD(nhead);
124
125 list_for_each_entry_rcu(orp, ohead, list) {
126 nrp = kzalloc(sizeof(struct smack_rule), gfp);
127 if (nrp == NULL) {
128 rc = -ENOMEM;
129 break;
130 }
131 *nrp = *orp;
132 list_add_rcu(&nrp->list, nhead);
133 }
134 return rc;
135}
136
87/* 137/*
88 * LSM hooks. 138 * LSM hooks.
89 * We he, that is fun! 139 * We he, that is fun!
@@ -102,23 +152,17 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode)
102{ 152{
103 int rc; 153 int rc;
104 struct smk_audit_info ad; 154 struct smk_audit_info ad;
105 char *sp, *tsp; 155 char *tsp;
106 156
107 rc = cap_ptrace_access_check(ctp, mode); 157 rc = cap_ptrace_access_check(ctp, mode);
108 if (rc != 0) 158 if (rc != 0)
109 return rc; 159 return rc;
110 160
111 sp = smk_of_current();
112 tsp = smk_of_task(task_security(ctp)); 161 tsp = smk_of_task(task_security(ctp));
113 smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); 162 smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
114 smk_ad_setfield_u_tsk(&ad, ctp); 163 smk_ad_setfield_u_tsk(&ad, ctp);
115 164
116 /* we won't log here, because rc can be overriden */ 165 rc = smk_curacc(tsp, MAY_READWRITE, &ad);
117 rc = smk_access(sp, tsp, MAY_READWRITE, NULL);
118 if (rc != 0 && capable(CAP_MAC_OVERRIDE))
119 rc = 0;
120
121 smack_log(sp, tsp, MAY_READWRITE, rc, &ad);
122 return rc; 166 return rc;
123} 167}
124 168
@@ -134,23 +178,17 @@ static int smack_ptrace_traceme(struct task_struct *ptp)
134{ 178{
135 int rc; 179 int rc;
136 struct smk_audit_info ad; 180 struct smk_audit_info ad;
137 char *sp, *tsp; 181 char *tsp;
138 182
139 rc = cap_ptrace_traceme(ptp); 183 rc = cap_ptrace_traceme(ptp);
140 if (rc != 0) 184 if (rc != 0)
141 return rc; 185 return rc;
142 186
187 tsp = smk_of_task(task_security(ptp));
143 smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); 188 smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
144 smk_ad_setfield_u_tsk(&ad, ptp); 189 smk_ad_setfield_u_tsk(&ad, ptp);
145 190
146 sp = smk_of_current(); 191 rc = smk_curacc(tsp, MAY_READWRITE, &ad);
147 tsp = smk_of_task(task_security(ptp));
148 /* we won't log here, because rc can be overriden */
149 rc = smk_access(tsp, sp, MAY_READWRITE, NULL);
150 if (rc != 0 && has_capability(ptp, CAP_MAC_OVERRIDE))
151 rc = 0;
152
153 smack_log(tsp, sp, MAY_READWRITE, rc, &ad);
154 return rc; 192 return rc;
155} 193}
156 194
@@ -474,7 +512,7 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
474{ 512{
475 char *isp = smk_of_inode(inode); 513 char *isp = smk_of_inode(inode);
476 char *dsp = smk_of_inode(dir); 514 char *dsp = smk_of_inode(dir);
477 u32 may; 515 int may;
478 516
479 if (name) { 517 if (name) {
480 *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_KERNEL); 518 *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_KERNEL);
@@ -483,14 +521,17 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
483 } 521 }
484 522
485 if (value) { 523 if (value) {
486 may = smk_access_entry(smk_of_current(), dsp); 524 rcu_read_lock();
525 may = smk_access_entry(smk_of_current(), dsp, &smack_rule_list);
526 rcu_read_unlock();
487 527
488 /* 528 /*
489 * If the access rule allows transmutation and 529 * If the access rule allows transmutation and
490 * the directory requests transmutation then 530 * the directory requests transmutation then
491 * by all means transmute. 531 * by all means transmute.
492 */ 532 */
493 if (((may & MAY_TRANSMUTE) != 0) && smk_inode_transmutable(dir)) 533 if (may > 0 && ((may & MAY_TRANSMUTE) != 0) &&
534 smk_inode_transmutable(dir))
494 isp = dsp; 535 isp = dsp;
495 536
496 *value = kstrdup(isp, GFP_KERNEL); 537 *value = kstrdup(isp, GFP_KERNEL);
@@ -716,7 +757,8 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name,
716 if (strcmp(name, XATTR_NAME_SMACK) == 0 || 757 if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
717 strcmp(name, XATTR_NAME_SMACKIPIN) == 0 || 758 strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
718 strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 || 759 strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 ||
719 strcmp(name, XATTR_NAME_SMACKEXEC) == 0) { 760 strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
761 strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
720 if (!capable(CAP_MAC_ADMIN)) 762 if (!capable(CAP_MAC_ADMIN))
721 rc = -EPERM; 763 rc = -EPERM;
722 /* 764 /*
@@ -773,6 +815,12 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
773 isp->smk_task = nsp; 815 isp->smk_task = nsp;
774 else 816 else
775 isp->smk_task = smack_known_invalid.smk_known; 817 isp->smk_task = smack_known_invalid.smk_known;
818 } else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
819 nsp = smk_import(value, size);
820 if (nsp != NULL)
821 isp->smk_mmap = nsp;
822 else
823 isp->smk_mmap = smack_known_invalid.smk_known;
776 } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) 824 } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0)
777 isp->smk_flags |= SMK_INODE_TRANSMUTE; 825 isp->smk_flags |= SMK_INODE_TRANSMUTE;
778 826
@@ -815,7 +863,8 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name)
815 strcmp(name, XATTR_NAME_SMACKIPIN) == 0 || 863 strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
816 strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 || 864 strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 ||
817 strcmp(name, XATTR_NAME_SMACKEXEC) == 0 || 865 strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
818 strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) { 866 strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0 ||
867 strcmp(name, XATTR_NAME_SMACKMMAP)) {
819 if (!capable(CAP_MAC_ADMIN)) 868 if (!capable(CAP_MAC_ADMIN))
820 rc = -EPERM; 869 rc = -EPERM;
821 } else 870 } else
@@ -829,6 +878,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name)
829 if (rc == 0) { 878 if (rc == 0) {
830 isp = dentry->d_inode->i_security; 879 isp = dentry->d_inode->i_security;
831 isp->smk_task = NULL; 880 isp->smk_task = NULL;
881 isp->smk_mmap = NULL;
832 } 882 }
833 883
834 return rc; 884 return rc;
@@ -1060,6 +1110,113 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd,
1060} 1110}
1061 1111
1062/** 1112/**
1113 * smk_mmap_list_check - the mmap check
1114 * @sub: subject label
1115 * @obj: object label
1116 * @access: access mode
1117 * @local: the task specific rule list
1118 *
1119 * Returns 0 if acces is permitted, -EACCES otherwise
1120 */
1121static int smk_mmap_list_check(char *sub, char *obj, int access,
1122 struct list_head *local)
1123{
1124 int may;
1125
1126 /*
1127 * If there is not a global rule that
1128 * allows access say no.
1129 */
1130 may = smk_access_entry(sub, obj, &smack_rule_list);
1131 if (may == -ENOENT || (may & access) != access)
1132 return -EACCES;
1133 /*
1134 * If there is a task local rule that
1135 * denies access say no.
1136 */
1137 may = smk_access_entry(sub, obj, local);
1138 if (may != -ENOENT && (may & access) != access)
1139 return -EACCES;
1140
1141 return 0;
1142}
1143
1144/**
1145 * smack_file_mmap :
1146 * Check permissions for a mmap operation. The @file may be NULL, e.g.
1147 * if mapping anonymous memory.
1148 * @file contains the file structure for file to map (may be NULL).
1149 * @reqprot contains the protection requested by the application.
1150 * @prot contains the protection that will be applied by the kernel.
1151 * @flags contains the operational flags.
1152 * Return 0 if permission is granted.
1153 */
1154static int smack_file_mmap(struct file *file,
1155 unsigned long reqprot, unsigned long prot,
1156 unsigned long flags, unsigned long addr,
1157 unsigned long addr_only)
1158{
1159 struct smack_rule *srp;
1160 struct task_smack *tsp;
1161 char *sp;
1162 char *msmack;
1163 struct inode_smack *isp;
1164 struct dentry *dp;
1165 int rc;
1166
1167 /* do DAC check on address space usage */
1168 rc = cap_file_mmap(file, reqprot, prot, flags, addr, addr_only);
1169 if (rc || addr_only)
1170 return rc;
1171
1172 if (file == NULL || file->f_dentry == NULL)
1173 return 0;
1174
1175 dp = file->f_dentry;
1176
1177 if (dp->d_inode == NULL)
1178 return 0;
1179
1180 isp = dp->d_inode->i_security;
1181 if (isp->smk_mmap == NULL)
1182 return 0;
1183 msmack = isp->smk_mmap;
1184
1185 tsp = current_security();
1186 sp = smk_of_current();
1187 rc = 0;
1188
1189 rcu_read_lock();
1190 /*
1191 * For each Smack rule associated with the subject
1192 * label verify that the SMACK64MMAP also has access
1193 * to that rule's object label.
1194 *
1195 * Because neither of the labels comes
1196 * from the networking code it is sufficient
1197 * to compare pointers.
1198 */
1199 list_for_each_entry_rcu(srp, &smack_rule_list, list) {
1200 if (srp->smk_subject != sp)
1201 continue;
1202 /*
1203 * Matching labels always allows access.
1204 */
1205 if (msmack == srp->smk_object)
1206 continue;
1207
1208 rc = smk_mmap_list_check(msmack, srp->smk_object,
1209 srp->smk_access, &tsp->smk_rules);
1210 if (rc != 0)
1211 break;
1212 }
1213
1214 rcu_read_unlock();
1215
1216 return rc;
1217}
1218
1219/**
1063 * smack_file_set_fowner - set the file security blob value 1220 * smack_file_set_fowner - set the file security blob value
1064 * @file: object in question 1221 * @file: object in question
1065 * 1222 *
@@ -1095,6 +1252,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk,
1095 * struct fown_struct is never outside the context of a struct file 1252 * struct fown_struct is never outside the context of a struct file
1096 */ 1253 */
1097 file = container_of(fown, struct file, f_owner); 1254 file = container_of(fown, struct file, f_owner);
1255
1098 /* we don't log here as rc can be overriden */ 1256 /* we don't log here as rc can be overriden */
1099 rc = smk_access(file->f_security, tsp, MAY_WRITE, NULL); 1257 rc = smk_access(file->f_security, tsp, MAY_WRITE, NULL);
1100 if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE)) 1258 if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE))
@@ -1145,9 +1303,14 @@ static int smack_file_receive(struct file *file)
1145 */ 1303 */
1146static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp) 1304static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
1147{ 1305{
1148 cred->security = kzalloc(sizeof(struct task_smack), gfp); 1306 struct task_smack *tsp;
1149 if (cred->security == NULL) 1307
1308 tsp = new_task_smack(NULL, NULL, gfp);
1309 if (tsp == NULL)
1150 return -ENOMEM; 1310 return -ENOMEM;
1311
1312 cred->security = tsp;
1313
1151 return 0; 1314 return 0;
1152} 1315}
1153 1316
@@ -1156,13 +1319,24 @@ static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
1156 * smack_cred_free - "free" task-level security credentials 1319 * smack_cred_free - "free" task-level security credentials
1157 * @cred: the credentials in question 1320 * @cred: the credentials in question
1158 * 1321 *
1159 * Smack isn't using copies of blobs. Everyone
1160 * points to an immutable list. The blobs never go away.
1161 * There is no leak here.
1162 */ 1322 */
1163static void smack_cred_free(struct cred *cred) 1323static void smack_cred_free(struct cred *cred)
1164{ 1324{
1165 kfree(cred->security); 1325 struct task_smack *tsp = cred->security;
1326 struct smack_rule *rp;
1327 struct list_head *l;
1328 struct list_head *n;
1329
1330 if (tsp == NULL)
1331 return;
1332 cred->security = NULL;
1333
1334 list_for_each_safe(l, n, &tsp->smk_rules) {
1335 rp = list_entry(l, struct smack_rule, list);
1336 list_del(&rp->list);
1337 kfree(rp);
1338 }
1339 kfree(tsp);
1166} 1340}
1167 1341
1168/** 1342/**
@@ -1178,13 +1352,16 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
1178{ 1352{
1179 struct task_smack *old_tsp = old->security; 1353 struct task_smack *old_tsp = old->security;
1180 struct task_smack *new_tsp; 1354 struct task_smack *new_tsp;
1355 int rc;
1181 1356
1182 new_tsp = kzalloc(sizeof(struct task_smack), gfp); 1357 new_tsp = new_task_smack(old_tsp->smk_task, old_tsp->smk_task, gfp);
1183 if (new_tsp == NULL) 1358 if (new_tsp == NULL)
1184 return -ENOMEM; 1359 return -ENOMEM;
1185 1360
1186 new_tsp->smk_task = old_tsp->smk_task; 1361 rc = smk_copy_rules(&new_tsp->smk_rules, &old_tsp->smk_rules, gfp);
1187 new_tsp->smk_forked = old_tsp->smk_task; 1362 if (rc != 0)
1363 return rc;
1364
1188 new->security = new_tsp; 1365 new->security = new_tsp;
1189 return 0; 1366 return 0;
1190} 1367}
@@ -1203,6 +1380,11 @@ static void smack_cred_transfer(struct cred *new, const struct cred *old)
1203 1380
1204 new_tsp->smk_task = old_tsp->smk_task; 1381 new_tsp->smk_task = old_tsp->smk_task;
1205 new_tsp->smk_forked = old_tsp->smk_task; 1382 new_tsp->smk_forked = old_tsp->smk_task;
1383 mutex_init(&new_tsp->smk_rules_lock);
1384 INIT_LIST_HEAD(&new_tsp->smk_rules);
1385
1386
1387 /* cbs copy rule list */
1206} 1388}
1207 1389
1208/** 1390/**
@@ -2419,6 +2601,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
2419 } 2601 }
2420 } 2602 }
2421 isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp); 2603 isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp);
2604 isp->smk_mmap = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp);
2422 2605
2423 dput(dp); 2606 dput(dp);
2424 break; 2607 break;
@@ -2478,6 +2661,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
2478static int smack_setprocattr(struct task_struct *p, char *name, 2661static int smack_setprocattr(struct task_struct *p, char *name,
2479 void *value, size_t size) 2662 void *value, size_t size)
2480{ 2663{
2664 int rc;
2481 struct task_smack *tsp; 2665 struct task_smack *tsp;
2482 struct task_smack *oldtsp; 2666 struct task_smack *oldtsp;
2483 struct cred *new; 2667 struct cred *new;
@@ -2513,13 +2697,16 @@ static int smack_setprocattr(struct task_struct *p, char *name,
2513 new = prepare_creds(); 2697 new = prepare_creds();
2514 if (new == NULL) 2698 if (new == NULL)
2515 return -ENOMEM; 2699 return -ENOMEM;
2516 tsp = kzalloc(sizeof(struct task_smack), GFP_KERNEL); 2700
2701 tsp = new_task_smack(newsmack, oldtsp->smk_forked, GFP_KERNEL);
2517 if (tsp == NULL) { 2702 if (tsp == NULL) {
2518 kfree(new); 2703 kfree(new);
2519 return -ENOMEM; 2704 return -ENOMEM;
2520 } 2705 }
2521 tsp->smk_task = newsmack; 2706 rc = smk_copy_rules(&tsp->smk_rules, &oldtsp->smk_rules, GFP_KERNEL);
2522 tsp->smk_forked = oldtsp->smk_forked; 2707 if (rc != 0)
2708 return rc;
2709
2523 new->security = tsp; 2710 new->security = tsp;
2524 commit_creds(new); 2711 commit_creds(new);
2525 return size; 2712 return size;
@@ -3221,6 +3408,7 @@ struct security_operations smack_ops = {
3221 .file_ioctl = smack_file_ioctl, 3408 .file_ioctl = smack_file_ioctl,
3222 .file_lock = smack_file_lock, 3409 .file_lock = smack_file_lock,
3223 .file_fcntl = smack_file_fcntl, 3410 .file_fcntl = smack_file_fcntl,
3411 .file_mmap = smack_file_mmap,
3224 .file_set_fowner = smack_file_set_fowner, 3412 .file_set_fowner = smack_file_set_fowner,
3225 .file_send_sigiotask = smack_file_send_sigiotask, 3413 .file_send_sigiotask = smack_file_send_sigiotask,
3226 .file_receive = smack_file_receive, 3414 .file_receive = smack_file_receive,
@@ -3334,23 +3522,20 @@ static __init int smack_init(void)
3334 struct cred *cred; 3522 struct cred *cred;
3335 struct task_smack *tsp; 3523 struct task_smack *tsp;
3336 3524
3337 tsp = kzalloc(sizeof(struct task_smack), GFP_KERNEL); 3525 if (!security_module_enable(&smack_ops))
3526 return 0;
3527
3528 tsp = new_task_smack(smack_known_floor.smk_known,
3529 smack_known_floor.smk_known, GFP_KERNEL);
3338 if (tsp == NULL) 3530 if (tsp == NULL)
3339 return -ENOMEM; 3531 return -ENOMEM;
3340 3532
3341 if (!security_module_enable(&smack_ops)) {
3342 kfree(tsp);
3343 return 0;
3344 }
3345
3346 printk(KERN_INFO "Smack: Initializing.\n"); 3533 printk(KERN_INFO "Smack: Initializing.\n");
3347 3534
3348 /* 3535 /*
3349 * Set the security state for the initial task. 3536 * Set the security state for the initial task.
3350 */ 3537 */
3351 cred = (struct cred *) current->cred; 3538 cred = (struct cred *) current->cred;
3352 tsp->smk_forked = smack_known_floor.smk_known;
3353 tsp->smk_task = smack_known_floor.smk_known;
3354 cred->security = tsp; 3539 cred->security = tsp;
3355 3540
3356 /* initialize the smack_know_list */ 3541 /* initialize the smack_know_list */